Browse Source

Atomic C# Work (+5 squashed commits)
Squashed commits:
[1c01a28] Resurrecting managed project builds
[f541a5a] Fixes for standalone editor
[1e33858] Xamarin Studio tweaks
[8472c04] Use defaults
[383b89c] Atomic C# Work (+6 squashed commits)
Squashed commits:
[f85e21a] Support for project naming with Solution/Assemblies
[ec53b83] Splitting out AtomicNETService to a separate folder, only build Deskop unless specified,
[e6f3455] Updates for standalone editor and new .NET project stuff
[ec5a0cb] Support project platform settings
[3a9eecb] Adding Android SDL java release aar
[77a1610] Atomic C# Work (+12 squashed commits)
Squashed commits:
[b9d8f45] C# Work
[85719e4] Exposing JoystickState to script
[ffc9901] AtomicNET Butterflies running on Android
[67f9a4e] C# Work
[2644935] C# Work
[a18da4c] Portable Class LIbrary build (WIP)
[1cd22dc] Checkpoint on Portable Class Library work
[6ed6b75] AtomicNET and AtomicAndroidSDL building from project generator
[4fcc370] C# Project gen support for Java binding libraries
[c1ea842] Adding Android C# Java SDL library project
[03c334d] Android AtomicNET assembly building
[a251d6c] Android C# native side building

Josh Engebretson 9 years ago
parent
commit
9a0ac4aa9e
100 changed files with 3552 additions and 879 deletions
  1. 1 1
      Build/CMake/Modules/AtomicPlatform.cmake
  2. 0 8
      Build/Scripts/BuildAndroid.js
  3. 8 8
      Build/Scripts/BuildCommon.js
  4. 4 0
      Build/Scripts/BuildLinux.js
  5. 6 1
      Build/Scripts/BuildMac.js
  6. 2 2
      Build/Scripts/BuildWindows.js
  7. 10 17
      Build/Scripts/HostCommon.js
  8. 0 22
      Script/AtomicNET/AtomicIPCPlayer/Program.cs
  9. 26 0
      Script/AtomicNET/AtomicNET/Application/AppBase.cs
  10. 21 0
      Script/AtomicNET/AtomicNET/Application/AppDelegate.cs
  11. 188 0
      Script/AtomicNET/AtomicNET/Application/AppOptions.cs
  12. 62 0
      Script/AtomicNET/AtomicNET/Application/Application.cs
  13. 1 6
      Script/AtomicNET/AtomicNET/Application/NETAtomicPlayer.cs
  14. 7 2
      Script/AtomicNET/AtomicNET/Application/NETIPCPlayerApp.cs
  15. 10 1
      Script/AtomicNET/AtomicNET/Application/NETServiceApplication.cs
  16. 0 79
      Script/AtomicNET/AtomicNET/Application/PlayerApp.cs
  17. 6 0
      Script/AtomicNET/AtomicNET/Core/AtomicNET.cs
  18. 4 3
      Script/AtomicNET/AtomicNET/Core/NativeCore.cs
  19. 1 7
      Script/AtomicNET/AtomicNET/Math/Rect.cs
  20. 36 0
      Script/AtomicNET/AtomicNET/Properties/AssemblyInfo.cs
  21. 31 21
      Script/AtomicNET/AtomicNET/Scene/CSComponentCore.cs
  22. 6 2
      Script/AtomicNET/AtomicNET/Scene/Node.cs
  23. 126 98
      Script/AtomicNET/AtomicNETProject.json
  24. 64 0
      Script/AtomicNET/AtomicPlayer/AtomicPlayer.Android/MainActivity.cs
  25. 18 0
      Script/AtomicNET/AtomicPlayer/AtomicPlayer.Desktop/Program.cs
  26. 0 20
      Script/AtomicNET/AtomicPlayer/Program.cs
  27. 89 0
      Script/AtomicNET/AtomicProject.json
  28. 143 0
      Script/AtomicNET/Platform/Android/AndroidSDLSurface.cs
  29. 10 0
      Script/AtomicNET/Platform/Android/JavaSDL/.gitignore
  30. 4 0
      Script/AtomicNET/Platform/Android/JavaSDL/Metadata.xml
  31. 1 0
      Script/AtomicNET/Platform/Android/JavaSDL/VERSION
  32. 59 0
      Script/AtomicNET/Platform/Android/JavaSDL/build.gradle
  33. 8 0
      Script/AtomicNET/Platform/Android/JavaSDL/compile
  34. BIN
      Script/AtomicNET/Platform/Android/JavaSDL/gradle/wrapper/gradle-wrapper.jar
  35. 6 0
      Script/AtomicNET/Platform/Android/JavaSDL/gradle/wrapper/gradle-wrapper.properties
  36. 160 0
      Script/AtomicNET/Platform/Android/JavaSDL/gradlew
  37. 90 0
      Script/AtomicNET/Platform/Android/JavaSDL/gradlew.bat
  38. 0 0
      Script/AtomicNET/Platform/Android/JavaSDL/proguard.txt
  39. 36 0
      Script/AtomicNET/Platform/Android/JavaSDL/release
  40. 0 0
      Script/AtomicNET/Platform/Android/JavaSDL/settings.gradle
  41. 30 0
      Script/AtomicNET/Platform/Android/JavaSDL/setup
  42. 23 0
      Script/AtomicNET/Platform/Android/JavaSDL/src/main/AndroidManifest.xml
  43. 60 0
      Script/AtomicNET/Platform/Android/JavaSDL/src/main/java/org/libsdl/app/AtomicActivity.java
  44. 1195 0
      Script/AtomicNET/Platform/Android/JavaSDL/src/main/java/org/libsdl/app/SDLActivity.java
  45. 397 0
      Script/AtomicNET/Platform/Android/JavaSDL/src/main/java/org/libsdl/app/SDLSurface.java
  46. 5 0
      Script/AtomicNET/Platform/Android/JavaSDL/src/main/res/values/values.xml
  47. BIN
      Script/AtomicNET/Platform/Android/JavaSDLBin/atomicjavasdl-release.aar
  48. 6 1
      Script/Packages/Atomic/Engine.json
  49. 3 2
      Script/Packages/Atomic/Input.json
  50. 5 1
      Script/Packages/AtomicApp/AtomicApp.json
  51. 1 2
      Script/Packages/AtomicApp/Package.json
  52. 7 2
      Script/Packages/AtomicNETNative/AtomicNETNative.json
  53. 1 1
      Script/Packages/AtomicNETNative/Package.json
  54. 1 1
      Script/Packages/AtomicNETScript/Package.json
  55. 4 4
      Source/Atomic/Graphics/BillboardSet.h
  56. 12 1
      Source/Atomic/IO/FileSystem.cpp
  57. 172 112
      Source/Atomic/Input/Input.cpp
  58. 18 2
      Source/Atomic/Input/Input.h
  59. 13 0
      Source/AtomicApp/AppBase.cpp
  60. 5 0
      Source/AtomicApp/AppBase.h
  61. 1 1
      Source/AtomicEditor/CMakeLists.txt
  62. 27 2
      Source/AtomicEditor/EditorMode/AEEditorMode.cpp
  63. 1 1
      Source/AtomicJS/CMakeLists.txt
  64. 38 5
      Source/AtomicNET/NETNative/CMakeLists.txt
  65. 9 23
      Source/AtomicNET/NETNative/Desktop/NETIPCPlayerApp.cpp
  66. 0 6
      Source/AtomicNET/NETNative/Desktop/NETIPCPlayerApp.h
  67. 0 17
      Source/AtomicNET/NETNative/Desktop/NETServiceApplication.cpp
  68. 0 4
      Source/AtomicNET/NETNative/Desktop/NETServiceApplication.h
  69. 26 18
      Source/AtomicNET/NETNative/NETAtomicPlayer.cpp
  70. 0 4
      Source/AtomicNET/NETNative/NETAtomicPlayer.h
  71. 3 0
      Source/AtomicNET/NETNative/NETCInterop.cpp
  72. 2 2
      Source/AtomicNET/NETScript/CMakeLists.txt
  73. 1 1
      Source/AtomicNET/NETScript/CSComponent.cpp
  74. 1 3
      Source/AtomicNET/NETScript/CSComponentAssembly.cpp
  75. 1 1
      Source/AtomicPlayerJS/CMakeLists.txt
  76. 5 1
      Source/AtomicTool/AtomicTool.cpp
  77. 2 2
      Source/AtomicTool/CMakeLists.txt
  78. 1 1
      Source/AtomicWebView/CMakeLists.txt
  79. 6 2
      Source/CMakeLists.txt
  80. 10 0
      Source/ThirdParty/SDL/src/core/android/SDL_android.c
  81. 0 1
      Source/ToolCore/Assets/Asset.cpp
  82. 14 2
      Source/ToolCore/Build/BuildBase.cpp
  83. 7 0
      Source/ToolCore/Build/BuildBase.h
  84. 25 24
      Source/ToolCore/Build/BuildWindows.cpp
  85. 1 1
      Source/ToolCore/Build/BuildWindows.h
  86. 1 0
      Source/ToolCore/Build/ResourcePackager.cpp
  87. 1 9
      Source/ToolCore/Command/BindCmd.cpp
  88. 0 1
      Source/ToolCore/Command/BindCmd.h
  89. 0 6
      Source/ToolCore/Command/BuildCmd.cpp
  90. 2 0
      Source/ToolCore/Command/Command.h
  91. 0 5
      Source/ToolCore/Command/CommandParser.cpp
  92. 83 71
      Source/ToolCore/Command/NETCmd.cpp
  93. 7 4
      Source/ToolCore/Command/NETCmd.h
  94. 0 95
      Source/ToolCore/Command/PlatformAddCmd.cpp
  95. 0 51
      Source/ToolCore/Command/PlatformAddCmd.h
  96. 19 81
      Source/ToolCore/JSBind/CSharp/CSFunctionWriter.cpp
  97. 40 1
      Source/ToolCore/JSBind/CSharp/CSModuleWriter.cpp
  98. 12 3
      Source/ToolCore/JSBind/CSharp/CSPackageWriter.cpp
  99. 3 2
      Source/ToolCore/JSBind/JSBEnum.cpp
  100. 1 1
      Source/ToolCore/JSBind/JSBHaxe.cpp

+ 1 - 1
Build/CMake/Modules/AtomicPlatform.cmake

@@ -25,7 +25,7 @@ elseif(EMSCRIPTEN)
 
 endif()
 
-set (JAVASCRIPT_BINDINGS_PLATFORM_ROOT "${CMAKE_SOURCE_DIR}/Artifacts/Build/Source/Generated/${JAVASCRIPT_BINDINGS_PLATFORM}")
+set (JAVASCRIPT_BINDINGS_PLATFORM_ROOT "${CMAKE_SOURCE_DIR}/Artifacts/Build/Source/Generated")
 
 if(NOT EXISTS "${JAVASCRIPT_BINDINGS_PLATFORM_ROOT}/Javascript")
 

+ 0 - 8
Build/Scripts/BuildAndroid.js

@@ -19,14 +19,6 @@ namespace('build', function() {
 
     var cmds = [];
 
-    var scriptModules = host.getScriptModules("ANDROID");
-    var bindCmd = host.atomicTool + " bind \"" + atomicRoot + "\" ";
-
-    // Generate bindings for each script package
-    for (var pkgName in scriptModules) {
-      cmds.push(bindCmd + "Script/Packages/" + pkgName + "/ ANDROID")
-    }
-
     if (os.platform() == "win32") {
       cmds.push(atomicRoot + "Build/Scripts/Windows/CompileAndroid.bat");
     }

+ 8 - 8
Build/Scripts/BuildCommon.js

@@ -51,18 +51,18 @@ namespace('build', function() {
     // precreate script bindgs so they can be picked up by CMake
     task('precreateScriptBindings', {
         async: true
-    }, function(platform, clean) {
+    }, function(clean) {
 
         if (clean === undefined) {
             clean = true;
         }
-        console.log("Precreating script bindings for platorm: " + platform);
+        console.log("Precreating script bindings");
 
         if (clean) {
-            common.cleanCreateDir(common.getGenScriptRootDir(platform))
+            common.cleanCreateDir(common.getGenScriptRootDir())
         }
 
-        common.createGenScriptFiles(platform);
+        common.createGenScriptFiles();
 
         complete();
     });
@@ -103,7 +103,7 @@ namespace('build', function() {
 
     task('genscripts', {
         async: true
-    }, function(platform, force) {
+    }, function(force) {
 
         // default to true
         if (force != "true" && force != "false") {
@@ -113,7 +113,7 @@ namespace('build', function() {
         var anyZero = false;
         if (force != "true") {
 
-            var filenames = common.getGenScriptFilenames(platform);
+            var filenames = common.getGenScriptFilenames();
             for (var i in filenames) {
 
                 if (!fileExists(filenames[i]))
@@ -138,7 +138,7 @@ namespace('build', function() {
 
         process.chdir(atomicRoot);
 
-        var modules = host.getScriptModules(platform);
+        var modules = host.getScriptModules();
         var bindCmd = host.atomicTool + " bind \"" + atomicRoot + "\" ";
         var node;
         var tsc = "./Build/node_modules/typescript/lib/tsc";
@@ -159,7 +159,7 @@ namespace('build', function() {
 
         var cmds = [];
         for (var pkgName in modules) {
-            cmds.push(bindCmd + "Script/Packages/" + pkgName + "/ " + platform);
+            cmds.push(bindCmd + "Script/Packages/" + pkgName + "/");
         }
 
         if (node) {

+ 4 - 0
Build/Scripts/BuildLinux.js

@@ -66,8 +66,12 @@ namespace('build', function() {
             // AtomicNET
 
             if (buildAtomicNET) {
+
               fs.copySync(atomicRoot + "Artifacts/AtomicNET/Release",
                  editorAppFolder + "Resources/ToolData/AtomicNET/Release");
+
+              fs.copySync(atomicRoot + "Script/AtomicNET/AtomicProject.json",
+                 editorAppFolder + "Resources/ToolData/AtomicNET/Build/Projects/AtomicProject.json");
             }
 
             var binaryFiles = ["chrome-sandbox", "libcef.so", "natives_blob.bin", "snapshot_blob.bin"];

+ 6 - 1
Build/Scripts/BuildMac.js

@@ -63,10 +63,15 @@ task('atomiceditor', {
       resourceDest + "ToolData/Deployment/MacOS/AtomicPlayer.app/Contents/MacOS/AtomicPlayer");
 
     // AtomicNET
-    if (buildAtomicNET)
+    if (buildAtomicNET) {
+        
       fs.copySync(atomicRoot + "Artifacts/AtomicNET/Release",
         resourceDest + "ToolData/AtomicNET/Release");
 
+      fs.copySync(atomicRoot + "Script/AtomicNET/AtomicProject.json",
+        resourceDest + "ToolData/AtomicNET/Build/Projects/AtomicProject.json");
+    }
+
     console.log("\n\nAtomic Editor build to " + editorAppFolder + "\n\n");
 
     complete();

+ 2 - 2
Build/Scripts/BuildWindows.js

@@ -61,8 +61,8 @@ namespace('build', function() {
       fs.copySync(atomicRoot + "Artifacts/AtomicNET/Release",
         editorAppFolder + "Resources/ToolData/AtomicNET/Release");
 
-      fs.copySync(buildDir +  "Source/AtomicPlayer/Application/Release/D3DCompiler_47.dll",
-        editorAppFolder + "Resources/ToolData/AtomicNET/Release/D3DCompiler_47.dll");
+      fs.copySync(atomicRoot + "Script/AtomicNET/AtomicProject.json",
+        editorAppFolder + "Resources/ToolData/AtomicNET/Build/Projects/AtomicProject.json");
 
       console.log("Atomic Editor build to ", editorAppFolder);
 

+ 10 - 17
Build/Scripts/HostCommon.js

@@ -9,7 +9,7 @@ process.env.NODE_PATH = atomicRoot + "Build/node_modules/";
 require('module').Module._initPaths();
 var fs = require('fs-extra');
 
-/// Returns a list of all script packages, regardless of platform
+/// Returns a list of all script packages
 function getScriptPackages() {
 
   var srcpath = atomicRoot + "Script/Packages/";
@@ -21,7 +21,7 @@ function getScriptPackages() {
 }
 
 // return an object with package name keys and module name lists as values
-function getScriptModules(platform) {
+function getScriptModules() {
 
   modules = {};
 
@@ -31,17 +31,10 @@ function getScriptModules(platform) {
 
     var pkg = JSON.parse(fs.readFileSync(atomicRoot + "Script/Packages/" + packages[i] + "/Package.json"));
 
-    if (pkg.platforms && pkg.platforms.indexOf(platform) == -1)
-      continue;
-
     for (var j in pkg.modules) {
 
       var moduleName = pkg.modules[j];
 
-      if (pkg.moduleExclude && pkg.moduleExclude[platform])
-      if (pkg.moduleExclude[platform].indexOf(moduleName) != -1)
-        continue;
-
       if (!modules[pkg.name]) {
         modules[pkg.name] = {
           moduleNames: [],
@@ -58,20 +51,20 @@ function getScriptModules(platform) {
 
 }
 
-function getGenScriptRootDir(platform) {
+function getGenScriptRootDir() {
 
-  return atomicRoot + "Artifacts/Build/Source/Generated/" + platform + "/";
+  return atomicRoot + "Artifacts/Build/Source/Generated/";
 
 }
 
-// Get a list of script source filenames for a given platform
-function getGenScriptFilenames(platform) {
+// Get a list of script source filenames
+function getGenScriptFilenames() {
 
   var filenames = [];
 
-  var scriptGenRoot = getGenScriptRootDir(platform);
+  var scriptGenRoot = getGenScriptRootDir();
 
-  var modules = getScriptModules(platform);
+  var modules = getScriptModules();
 
   for (var pkgName in modules) {
 
@@ -113,9 +106,9 @@ function getGenScriptFilenames(platform) {
 
 }
 
-function createGenScriptFiles(platform) {
+function createGenScriptFiles() {
 
-  var scriptFiles = getGenScriptFilenames(platform);
+  var scriptFiles = getGenScriptFilenames();
 
   for (var i in scriptFiles) {
 

+ 0 - 22
Script/AtomicNET/AtomicIPCPlayer/Program.cs

@@ -1,22 +0,0 @@
-
-using AtomicEngine;
-
-public class Program
-{
-    public static void Main(string[] args)
-    {               
-        // Create the Application
-        var app = NETIPCPlayerApp.Create(args);
-
-        // Managed code in charge of main loop
-        while (app.RunFrame())
-        {
-
-        }
-
-        // Shut 'er down
-        app.Shutdown();
-    }
-}
-
-

+ 26 - 0
Script/AtomicNET/AtomicNET/Application/AppBase.cs

@@ -0,0 +1,26 @@
+
+
+namespace AtomicEngine
+{
+
+    public partial class AppBase : Application
+    {
+
+        public virtual int Run()
+        {
+
+            // Managed code in charge of main loop
+            while (RunFrame())
+            {
+
+            }
+
+            // Shut 'er down
+            Shutdown();
+
+            return 0;
+        }
+
+    }
+
+}

+ 21 - 0
Script/AtomicNET/AtomicNET/Application/AppDelegate.cs

@@ -0,0 +1,21 @@
+using System;
+using System.Reflection;
+using System.Linq;
+
+namespace AtomicEngine
+{
+
+    public abstract class AppDelegate : NETScriptObject
+    {
+
+        public virtual void Start() { }
+
+        // called after application RunFrame
+        public virtual void PostFrame() { }
+
+        public virtual void Shutdown() { }
+
+    }
+
+
+}

+ 188 - 0
Script/AtomicNET/AtomicNET/Application/AppOptions.cs

@@ -0,0 +1,188 @@
+/*
+Portions Copyright(c) 2015 Xamarin Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+using System;
+using System.Linq;
+using System.Text;
+
+namespace AtomicEngine
+{
+    /// <summary>
+    /// Application options, see full description at:
+    /// http://urho3d.github.io/documentation/1.5/_running.html 
+    /// </summary>
+    public class AppOptions
+    {
+        /// <param name="assetsFolder">usually it's "Data". Can be null if built-in assets are enough for you</param>
+        public AppOptions(string assetsFolder)
+        {
+            if (assetsFolder != null)
+            {
+                ResourcePaths = new[] { assetsFolder };
+            }
+        }
+
+        public AppOptions(string[] assetsFolders)
+        {
+            ResourcePaths = assetsFolders;
+        }
+
+        /// <summary>
+        /// Desktop only
+        /// </summary>
+        public int Width { get; set; } = 0;
+
+        /// <summary>
+        /// Desktop only
+        /// </summary>
+        public int Height { get; set; } = 0;
+
+        /// <summary>
+        /// Desktop only
+        /// </summary>
+        public bool WindowedMode { get; set; } = true;
+
+        /// <summary>
+        /// Desktop only
+        /// </summary>
+        public bool ResizableWindow { get; set; } = false;
+
+        /// <summary>
+        /// With limit enabled: 200 fps for Desktop (and always 60 fps for mobile despite of the flag)
+        /// </summary>
+        public bool LimitFps { get; set; } = true;
+
+#if XFORMS
+		/// <summary>
+		/// iOS only
+		/// </summary>
+		public OrientationType Orientation { get; set; } = OrientationType.Portrait;
+#else
+        /// <summary>
+        /// iOS only
+        /// </summary>
+        public OrientationType Orientation { get; set; } = OrientationType.Landscape;
+#endif
+        /// <summary>
+        /// Resource path(s) to use (default: Data, CoreData)
+        /// </summary>
+        public string[] ResourcePaths { get; set; } = null;
+
+        /// <summary>
+        /// Resource package files to use (default: empty)
+        /// </summary>
+        public string[] ResourcePackagesPaths { get; set; } = null;
+
+#if WINDOWS_UWP
+		public bool TouchEmulation { get { return true; } set {} }
+#else
+        /// <summary>
+        /// Touch emulation on desktop platform
+        /// </summary>
+        public bool TouchEmulation { get; set; } = false;
+#endif
+
+        /// <summary>
+        /// Add any flag listed here: http://urho3d.github.io/documentation/1.5/_running.html 
+        /// </summary>
+        public string AdditionalFlags { get; set; } = string.Empty;
+
+        /// <summary>
+        /// Windows: external window handle (WinForms Panel.Handle) to use in order to display Urho game
+        /// You can use it in WPF via WindowsFormsHost (and a WF panel inside it)
+        /// </summary>
+        public IntPtr ExternalWindow { get; set; }
+
+        public bool DelayedStart { get; set; } = false;
+
+        public bool AutoloadCoreData { get; set; } = true;
+
+        public string[] ResourcePrefixPaths { get; set; }
+
+        public enum OrientationType
+        {
+            Landscape,
+            Portrait,
+            LandscapeAndPortrait
+        }
+
+        public override string ToString()
+        {
+            StringBuilder builder = new StringBuilder();
+            builder.Append("args");//it will be skipped by Urho;
+
+#if !ATOMIC_IOS //always use -w on iOS
+            if (WindowedMode)
+#endif
+                builder.Append(" -w");
+
+            if (!LimitFps)
+                builder.Append(" -nolimit");
+
+            if (DelayedStart)
+                builder.Append(" -delayedstart");
+
+            if (Width > 0)
+                builder.AppendFormat(" -x {0}", Width);
+
+            if (Height > 0)
+                builder.AppendFormat(" -y {0}", Height);
+
+#if !ATOMIC_IOS //always use -s on iOS and UWP
+            if (ResizableWindow)
+#endif
+                builder.Append(" -s");
+
+            string[] resourcePathes = new[] { "CoreData" }.Concat(ResourcePaths ?? new string[0]).ToArray();
+            if (!AutoloadCoreData)
+                resourcePathes = ResourcePaths ?? new string[0];
+            builder.AppendFormat(" -p \"{0}\"", string.Join(";", resourcePathes.Distinct()));
+
+            if (ResourcePackagesPaths?.Length > 0)
+                builder.AppendFormat(" -pf \"{0}\"", string.Join(";", ResourcePackagesPaths));
+
+            if (ResourcePrefixPaths?.Length > 0)
+                builder.AppendFormat(" -pp \"{0}\"", string.Join(";", ResourcePrefixPaths));
+
+#if !ATOMIC_WINDOWS_UWP
+            if (TouchEmulation)
+#endif
+                builder.Append(" -touch");
+
+            switch (Orientation)
+            {
+                case OrientationType.Landscape:
+                    builder.Append(" -landscape");
+                    break;
+                case OrientationType.Portrait:
+                    builder.Append(" -portrait");
+                    break;
+                case OrientationType.LandscapeAndPortrait:
+                    builder.Append(" -landscape -portrait");
+                    break;
+            }
+
+            return builder + " " + AdditionalFlags;
+        }
+    }
+}

+ 62 - 0
Script/AtomicNET/AtomicNET/Application/Application.cs

@@ -0,0 +1,62 @@
+
+using System;
+
+namespace AtomicEngine
+{
+
+    public interface IAtomicSDLSurface
+    {
+        void Remove();
+        bool IsAlive { get; }
+    }
+
+    public partial class Application : AObject
+    {
+
+        public static int Run<T>(string[] args) where T : AppDelegate, new()
+        {
+            return Run(typeof(T), args);
+        }
+
+        public static int Run(Type appDelegateType, string[] args)
+        {
+            // Create the Application
+
+            AppBase app = null;
+
+#if ATOMIC_DESKTOP
+            app = NETIPCPlayerApp.Create(args);
+#endif
+
+#if ATOMIC_ANDROID
+            app = NETAtomicPlayer.Create(args);
+            
+            var renderer = AtomicNET.GetSubsystem<Renderer>();
+            renderer.ReuseShadowMaps = false;
+            renderer.ShadowQuality = ShadowQuality.SHADOWQUALITY_SIMPLE_16BIT;
+#endif
+
+            appDelegate = (AppDelegate)Activator.CreateInstance(appDelegateType);
+
+            appDelegate.Start();
+
+            // Managed code in charge of main loop
+            while (app.RunFrame())
+            {
+                appDelegate.PostFrame();
+            }
+
+            appDelegate.Shutdown();
+
+            // Shut 'er down
+            app.Shutdown();
+
+            return 0;
+        }
+
+
+        internal static AppDelegate appDelegate;
+
+    }
+
+}

+ 1 - 6
Script/AtomicNET/AtomicNET/Application/NETAtomicPlayer.cs

@@ -7,12 +7,9 @@ namespace AtomicEngine
         ///  AtomicPlayer for NET deployments
         /// </summary>
         /// <param name="args"></param>
-        /// <param name="headless"></param>
         /// <returns></returns>
-        public static NETAtomicPlayer Create(string[] args, bool headless = false)
+        public static NETAtomicPlayer Create(string[] args)
         {
-            DeployedApp = true;
-
             // Initialize AtomicNET
             AtomicNET.Initialize();
 
@@ -22,8 +19,6 @@ namespace AtomicEngine
 
             RegisterSubsystems();
 
-            ExecuteAtomicMain(args);
-
             return app;
         }
 

+ 7 - 2
Script/AtomicNET/AtomicNET/Application/NETIPCPlayerApp.cs

@@ -1,3 +1,8 @@
+using System;
+using System.Reflection;
+
+#if ATOMIC_DESKTOP
+
 namespace AtomicEngine
 {
     public partial class NETIPCPlayerApp : IPCPlayerApp
@@ -18,8 +23,6 @@ namespace AtomicEngine
             app.Initialize();
 
             RegisterSubsystems();
-            
-            ExecuteAtomicMain(args);
 
             return app;
         }
@@ -28,3 +31,5 @@ namespace AtomicEngine
     }
 
 }
+
+#endif

+ 10 - 1
Script/AtomicNET/AtomicNET/Application/NETServiceApplication.cs

@@ -1,3 +1,10 @@
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+#if ATOMIC_DESKTOP
+
 namespace AtomicEngine
 {
 
@@ -20,4 +27,6 @@ namespace AtomicEngine
 
     }
 
-}
+}
+
+#endif

+ 0 - 79
Script/AtomicNET/AtomicNET/Application/PlayerApp.cs

@@ -8,7 +8,6 @@ namespace AtomicEngine
 
     public partial class PlayerApp : AppBase
     {
-        public static bool DeployedApp = false;
 
         static protected void RegisterSubsystems()
         {
@@ -24,84 +23,6 @@ namespace AtomicEngine
             AtomicNET.RegisterSubsystem("ResourceCache");
             AtomicNET.Cache = AtomicNET.GetSubsystem<ResourceCache>();
 
-            AppDomain currentDomain = AppDomain.CurrentDomain;
-            currentDomain.AssemblyResolve += new ResolveEventHandler(AtomicResolveEventHandler);
-
-        }
-
-        // Resolve assemblies from Resource directories at runtime (todo, assemblies in package files?)
-        static private Assembly AtomicResolveEventHandler(object sender, ResolveEventArgs args)
-        {
-            //This handler is called only when the common language runtime tries to bind to the assembly and fails.
-
-            string assemblyFileName = args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll";
-
-            for (uint i = 0; i < AtomicNET.Cache.NumResourceDirs; i++)
-            {
-                string[] assemblies = Directory.GetFiles(AtomicNET.Cache.GetResourceDir(i), "*.dll", SearchOption.AllDirectories);
-
-                for (int j = 0; j < assemblies.Length; j++)
-                {
-                    if (assemblies[j].Contains(assemblyFileName))
-                    {
-                        //Load the assembly from the specified path.                    
-                        Assembly loadAssembly = Assembly.LoadFrom(assemblies[j]);
-
-                        //Return the loaded assembly.
-                        return loadAssembly;
-
-                    }
-                }
-            }
-
-            return null;
-
-        }
-
-        protected static void ExecuteAtomicMain(string[] args)
-        {
-            String projectAssemblyPath;
-
-            if (!DeployedApp)
-            {
-                var file = AtomicNET.Cache.GetFile("AtomicProject.dll");
-                projectAssemblyPath = file.FullPath;
-            }
-            else
-            {
-                projectAssemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "/AtomicProject.dll";
-            }
-               
-            Assembly assembly = Assembly.LoadFrom(projectAssemblyPath);
-
-            if (assembly == null)
-                return;
-
-            Type atomicMainType = null;
-            Type[] assemblyTypes = assembly.GetTypes();
-
-            for (int j = 0; j < assemblyTypes.Length; j++)
-            {
-                if (assemblyTypes[j].Name == "AtomicMain")
-                {
-                    atomicMainType = assemblyTypes[j];
-                    break;
-                }
-            }
-
-            if (atomicMainType == null)
-                return;
-
-            Type[] mainParms = new Type[1] { typeof(string[]) };
-
-            var method = atomicMainType.GetMethod("Main", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, Type.DefaultBinder, mainParms, null);
-
-            if (method == null)
-                return;
-
-            Object[] parms = new Object[1] { args };
-            method.Invoke(null, parms);
-
         }
 
     }

+ 6 - 0
Script/AtomicNET/AtomicNET/Core/AtomicNET.cs

@@ -64,7 +64,11 @@ namespace AtomicEngine
             PhysicsModule.Initialize();
             EnvironmentModule.Initialize();
             UIModule.Initialize();
+
+#if ATOMIC_DESKTOP
             IPCModule.Initialize();
+#endif
+
             AtomicAppModule.Initialize();
             ScriptModule.Initialize();
 
@@ -89,9 +93,11 @@ namespace AtomicEngine
             NativeCore.Initialize();
             CSComponentCore.Initialize();
 
+#if ATOMIC_DESKTOP
             string[] arguments = Environment.GetCommandLineArgs();
             foreach (string arg in arguments)
                 AppBase.AddArgument(arg);
+#endif
 
         }
 

+ 4 - 3
Script/AtomicNET/AtomicNET/Core/NativeCore.cs

@@ -3,6 +3,7 @@ using System.Diagnostics;
 using System.Collections.Generic;
 using System.Runtime.InteropServices;
 using System.Linq;
+using static System.Reflection.IntrospectionExtensions;
 
 namespace AtomicEngine
 {
@@ -129,7 +130,7 @@ namespace AtomicEngine
 
             if (!eventReceiverLookup.TryGetValue(eventType, out eventReceivers))
             {
-                // This should not happen, as event NET objects are subscribed to are filtered 
+                // This should not happen, as event NET objects are subscribed to are filtered
                 throw new InvalidOperationException("NativeCore.EventDispatch - received unregistered event type");
 
             }
@@ -170,7 +171,7 @@ namespace AtomicEngine
                 {
                     receiver.HandleEvent(managedSender, eventType, scriptMap);
                 }
-                else
+                else if (er.Sender == IntPtr.Zero)
                 {
                     receiver.HandleEvent(eventType, scriptMap);
                 }
@@ -362,7 +363,7 @@ namespace AtomicEngine
             Type ancestorType = type;
             do
             {
-                ancestorType = ancestorType.BaseType;
+                ancestorType = ancestorType.GetTypeInfo().BaseType;
             } while (ancestorType != null && !IsNativeType(ancestorType));
 
             return ancestorType;

+ 1 - 7
Script/AtomicNET/AtomicNET/Math/Rect.cs

@@ -13,9 +13,6 @@ namespace AtomicEngine
             Min.Y = top;
             Max.X = right;
             Max.Y = bottom;
-
-            // for native
-            Defined = true;
         }
 
         public float Left
@@ -73,10 +70,7 @@ namespace AtomicEngine
         public Vector2 Min;
         
         /// Maximum vector.
-         public Vector2 Max;
-
-        /// Defined flag.
-        bool Defined;
+        public Vector2 Max;
 
     }
 

+ 36 - 0
Script/AtomicNET/AtomicNET/Properties/AssemblyInfo.cs

@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("AtomicNET")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("AtomicNET")]
+[assembly: AssemblyCopyright("Copyright © THUNDERBEAST GAMES LLC 2014-2016")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components.  If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("741996db-5c6c-4d33-88da-97bec0dcef86")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]

+ 31 - 21
Script/AtomicNET/AtomicNET/Scene/CSComponentCore.cs

@@ -10,6 +10,9 @@ namespace AtomicEngine
     {
         public CSComponentInfo(Type type)
         {
+
+#if ATOMIC_DESKTOP || ATOMIC_ANDROID
+
             this.Type = type;
 
             // Fields
@@ -45,6 +48,7 @@ namespace AtomicEngine
 
             Type[] startParms = new Type[0] { };
             StartMethod = type.GetMethod("Start", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, Type.DefaultBinder, startParms, null);
+#endif
 
         }
 
@@ -52,6 +56,8 @@ namespace AtomicEngine
         {
             // FIXME: This will need to be optimized, specifically to use uint key hashes for value lookup
 
+#if ATOMIC_DESKTOP || ATOMIC_ANDROID
+
             fieldMap.CopyVariantMap(fieldValuePtr);
 
             foreach (var field in InspectorFields)
@@ -108,6 +114,8 @@ namespace AtomicEngine
                 }
             }
 
+#endif
+
         }
 
         public void RegisterInstance(CSComponent component)
@@ -288,10 +296,10 @@ namespace AtomicEngine
 
         }
 
-
         void HandleComponentLoad(uint eventType, ScriptVariantMap eventData)
         {
             var assemblyPath = eventData["AssemblyPath"];
+
             var className = eventData["ClassName"];
             IntPtr csnative = eventData.GetVoidPtr("NativeInstance");
             IntPtr fieldValues = IntPtr.Zero;
@@ -324,43 +332,45 @@ namespace AtomicEngine
 
         }
 
-        void HandleComponentAssemblyReference(uint eventType, ScriptVariantMap eventData)
+        void ParseComponents()
         {
-            string assemblyPath = eventData["AssemblyPath"];
+#if ATOMIC_DESKTOP || ATOMIC_ANDROID
 
-            Dictionary<string, CSComponentInfo> assemblyTypes = null;
+            var assemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
 
-            if (!componentCache.TryGetValue(assemblyPath, out assemblyTypes))
+            foreach (Assembly assembly in assemblies)
             {
-                componentCache[assemblyPath] = assemblyTypes = new Dictionary<string, CSComponentInfo>();
-            }
+                String assemblyPath = assembly.GetName().Name;
 
-            // HACK!
-            if (PlayerApp.DeployedApp)
-            {
-                assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + "/AtomicProject.dll";
-            }
+                Dictionary<string, CSComponentInfo> assemblyTypes = null;
 
-            Assembly assembly = Assembly.LoadFrom(assemblyPath);
+                if (!componentCache.TryGetValue(assemblyPath, out assemblyTypes))
+                {
+                    componentCache[assemblyPath] = assemblyTypes = new Dictionary<string, CSComponentInfo>();
+                }
 
-            Type[] types = assembly.GetTypes();
+                Type[] types = assembly.GetTypes();
 
-            foreach (var type in types)
-            {
-                if (type.IsSubclassOf(typeof(CSComponent)))
+                foreach (var type in types)
                 {
-                    var csinfo = new CSComponentInfo(type);
-                    csinfoLookup[csinfo.Type] = csinfo;
-                    assemblyTypes[type.Name] = csinfo;
+                    if (type.IsSubclassOf(typeof(CSComponent)))
+                    {
+                        var csinfo = new CSComponentInfo(type);
+                        csinfoLookup[csinfo.Type] = csinfo;
+                        assemblyTypes[type.Name] = csinfo;
+                    }
                 }
+
             }
+#endif
         }
 
         internal static void Initialize()
         {
+
             instance = new CSComponentCore();
+            instance.ParseComponents();
             
-            instance.SubscribeToEvent("CSComponentAssemblyReference", instance.HandleComponentAssemblyReference);
             instance.SubscribeToEvent("CSComponentLoad", instance.HandleComponentLoad);
             instance.SubscribeToEvent("Update", instance.HandleUpdate);
 

+ 6 - 2
Script/AtomicNET/AtomicNET/Scene/Node.cs

@@ -1,4 +1,7 @@
 using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using static System.Reflection.IntrospectionExtensions;
 
 namespace AtomicEngine
 {
@@ -10,7 +13,7 @@ namespace AtomicEngine
         {
             var type = typeof(T);
 
-            if (type.IsSubclassOf(typeof(CSComponent)))
+            if (type.GetTypeInfo().IsSubclassOf(typeof(CSComponent)))
             {
                 Component component = (Component)Activator.CreateInstance(type);
                 CSComponentCore.RegisterInstance((CSComponent)component);
@@ -21,12 +24,13 @@ namespace AtomicEngine
             return (T)CreateComponent(type.Name, mode, id);
         }
 
+
         public void GetChildrenWithComponent<T>(Vector<Node> dest, bool recursive = false)
         {
             var type = typeof(T);
 
             // If we're a CSComponent, get "CSComponents" native side and filter here
-            if (type.IsSubclassOf(typeof(CSComponent)))
+            if (type.GetTypeInfo().IsSubclassOf(typeof(CSComponent)))
             {
                 Vector<Node> temp = new Vector<Node>();
 

+ 126 - 98
Script/AtomicNET/AtomicNETProject.json

@@ -1,102 +1,130 @@
 {
-  "solution" : {
-    "name" : "AtomicNET",
-    "outputPath" : "$ATOMIC_ROOT$/Artifacts/AtomicNET/Build/"
-  },
-  "projects" : [
-    {
-      "name": "AtomicNET",
-      "outputType" : "Library",
-      "rootNamespace" : "AtomicEngine",
-      "assemblyName" : "AtomicNET",
-      "assemblyOutputPath" : "..\\..\\",
-      "references" : [
-        "System", "System.Core" , "System.Xml.Linq", "System.XML"
-      ],
-      "sources" : [
-        "$ATOMIC_ROOT$/Script/AtomicNET/AtomicNETCore/",
-        "$ATOMIC_ROOT$/Script/AtomicNET/AtomicNET/",
-        "$ATOMIC_ROOT$/Artifacts/Build/Source/Generated/$SCRIPT_PLATFORM$/CSharp/Packages/Atomic/Managed/",
-        "$ATOMIC_ROOT$/Artifacts/Build/Source/Generated/$SCRIPT_PLATFORM$/CSharp/Packages/AtomicNETScript/Managed/",
-        "$ATOMIC_ROOT$/Artifacts/Build/Source/Generated/$SCRIPT_PLATFORM$/CSharp/Packages/AtomicNETNative/Managed/",
-        "$ATOMIC_ROOT$/Artifacts/Build/Source/Generated/$SCRIPT_PLATFORM$/CSharp/Packages/AtomicApp/Managed/",
-        "$ATOMIC_ROOT$/Artifacts/Build/Source/Generated/$SCRIPT_PLATFORM$/CSharp/Packages/AtomicPlayer/Managed/"
-      ]
+    "solution" : {
+        "name" : "AtomicNET",
+        "outputPath" : "$ATOMIC_ROOT$/Artifacts/AtomicNET/Build/"
     },
-    {
-      "name": "AtomicNETService",
-      "outputType" : "Exe",
-      "rootNamespace" : "AtomicTools",
-      "assemblyName" : "AtomicNETService",
-      "assemblyOutputPath" : "..\\..\\",
-      "references" : [
-        "System",
-        "<Reference Include=\"System.Collections.Immutable, Version=1.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\"><HintPath>..\\packages\\System.Collections.Immutable.1.2.0\\lib\\portable-net45+win8+wp8+wpa81\\System.Collections.Immutable.dll</HintPath><Private>True</Private></Reference>",
-        "System.Core" ,
-        "<Reference Include=\"System.Reflection.Metadata, Version=1.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\"><HintPath>..\\packages\\System.Reflection.Metadata.1.3.0\\lib\\portable-net45+win8\\System.Reflection.Metadata.dll</HintPath><Private>True</Private></Reference>",
-        "System.Xml.Linq",
-        "System.XML",
-        "AtomicNET"
-      ],
-      "sources" : [
-        "$ATOMIC_ROOT$/Script/AtomicNET/AtomicNETService/"
-      ],
-      "packages" : [
-        "<package id=\"System.Collections.Immutable\" version=\"1.2.0\" targetFramework=\"net46\" />",
-        "<package id=\"System.Reflection.Metadata\" version=\"1.3.0\" targetFramework=\"net46\" />"
-      ]
-    },
-    {
-      "name": "ComponentTest",
-      "outputType" : "Library",
-      "rootNamespace" : "ComponentTest",
-      "assemblyName" : "ComponentTest",
-      "assemblyOutputPath" : "..\\..\\",
-      "references" : [
-        "System",
-        "System.Core" ,
-        "System.Xml.Linq",
-        "System.XML",
-        "AtomicNET"
-      ],
-      "sources" : [
-        "$ATOMIC_ROOT$/Script/AtomicNET/ComponentTest/"
-      ]
-    },
-    {
-      "name": "AtomicIPCPlayer",
-      "outputType" : "Exe",
-      "rootNamespace" : "AtomicIPCPlayer",
-      "assemblyName" : "AtomicIPCPlayer",
-      "assemblyOutputPath" : "..\\..\\",
-      "references" : [
-        "System",
-        "System.Core" ,
-        "System.Xml.Linq",
-        "System.XML",
-        "AtomicNET"
-      ],
-      "sources" : [
-        "$ATOMIC_ROOT$/Script/AtomicNET/AtomicIPCPlayer/"
-      ]
-  },
-  {
-    "name": "AtomicPlayer",
-    "outputType" : "Exe",
-    "rootNamespace" : "AtomicPlayer",
-    "assemblyName" : "AtomicPlayer",
-    "assemblyOutputPath" : "..\\..\\",
-    "references" : [
-      "System",
-      "System.Core" ,
-      "System.Xml.Linq",
-      "System.XML",
-      "AtomicNET"
-    ],
-    "sources" : [
-      "$ATOMIC_ROOT$/Script/AtomicNET/AtomicPlayer/"
+    "projects" : [
+        {
+            "name": "AtomicNET.Shared",
+            "outputType" : "Shared",
+            "projectGuid" : "C74A8ECC-7517-11E6-A1D7-005056C00008",
+            "sources" : [
+                "$ATOMIC_ROOT$/Script/AtomicNET/AtomicNETCore/",
+                "$ATOMIC_ROOT$/Script/AtomicNET/AtomicNET/",
+                "$ATOMIC_ROOT$/Artifacts/Build/Source/Generated/CSharp/Packages/Atomic/Managed/",
+                "$ATOMIC_ROOT$/Artifacts/Build/Source/Generated/CSharp/Packages/AtomicNETScript/Managed/",
+                "$ATOMIC_ROOT$/Artifacts/Build/Source/Generated/CSharp/Packages/AtomicNETNative/Managed/",
+                "$ATOMIC_ROOT$/Artifacts/Build/Source/Generated/CSharp/Packages/AtomicApp/Managed/",
+                "$ATOMIC_ROOT$/Artifacts/Build/Source/Generated/CSharp/Packages/AtomicPlayer/Managed/"
+            ]
+        },
+        {
+            "name": "AtomicNET",
+            "outputType" : "Library",
+            "rootNamespace" : "AtomicEngine",
+            "assemblyName" : "AtomicNET",
+            "assemblyOutputPath" : "..\\..\\$ATOMIC_CONFIG$\\Portable\\",
+            "projectGuid" : "C74A8ECD-7517-11E6-9B0E-005056C00008",
+            "projectTypeGuids" : ["786C830F-07A1-408B-BD7F-6EE04809D6DB", "FAE04EC0-301F-11D3-BF4B-00C04F79EFBC"],
+            "targetFrameworkProfile" : "Profile111",
+            "references" : [
+            ],
+            "sharedReferences" : [
+                "AtomicNET.Shared"
+            ],
+            "importProjects" : ["$(MSBuildExtensionsPath32)\\Microsoft\\Portable\\$(TargetFrameworkVersion)\\Microsoft.Portable.CSharp.targets"],
+            "sources" : [
+            ]
+        },
+        {
+            "name": "AtomicNET.Desktop",
+            "platforms" : ["desktop"],
+            "outputType" : "Library",
+            "defineConstants" : ["ATOMIC_DESKTOP"],
+            "rootNamespace" : "AtomicGameEngine",
+            "assemblyName" : "AtomicNET",
+            "assemblyOutputPath" : "..\\..\\$ATOMIC_CONFIG$\\Desktop\\",
+            "projectGuid" : "C74AB5E6-7517-11E6-8979-005056C00008",
+            "references" : [
+                "System",
+                "System.Core",
+                "System.Xml.Linq",
+                "System.Xml"
+            ],
+            "sharedReferences" : [
+                "AtomicNET.Shared"
+            ],
+            "sources" : [
+            ]
+        },
+        {
+            "name": "AtomicNET.Android.SDL",
+            "platforms" : ["android"],
+            "outputType" : "Library",
+            "rootNamespace" : "AtomicGameEngine",
+            "assemblyName" : "AtomicNET.Android.SDL",
+            "projectTypeGuids" : ["10368E6C-D01B-4462-8E8B-01FC667A7035", "FAE04EC0-301F-11D3-BF4B-00C04F79EFBC"],
+            "importProjects" : ["$(MSBuildExtensionsPath)\\Xamarin\\Android\\Xamarin.Android.Bindings.targets"],
+            "libraryProjectZips" : ["$ATOMIC_ROOT$/Script/AtomicNET/Platform/Android/JavaSDLBin/atomicjavasdl-release.aar"],
+            "transformFiles" : ["$ATOMIC_ROOT$/Script/AtomicNET/Platform/Android/JavaSDL/Metadata.xml"],
+            "assemblyOutputPath" : "..\\..\\$ATOMIC_CONFIG$\\Android\\",
+            "projectGuid" : "C74AB5E7-7517-11E6-BF7F-005056C00008",
+            "references" : [
+                "Mono.Android",
+                "System",
+                "System.Core"
+            ],
+            "sources" : [
+            ]
+        },
+        {
+            "name": "AtomicNET.Android",
+            "platforms" : ["android"],
+            "outputType" : "Library",
+            "defineConstants" : ["ATOMIC_ANDROID"],
+            "rootNamespace" : "AtomicGameEngine",
+            "assemblyName" : "AtomicNET",
+            "assemblyOutputPath" : "..\\..\\$ATOMIC_CONFIG$\\Android\\",
+            "projectGuid" : "C74AB5E8-7517-11E6-B3CD-005056C00008",
+            "references" : [
+                "Mono.Android",
+                "System",
+                "System.Core",
+                "System.Xml.Linq",
+                "System.Xml",
+                "AtomicNET.Android.SDL"
+            ],
+            "sharedReferences" : [
+                "AtomicNET.Shared"
+            ],
+            "sources" : [
+                "$ATOMIC_ROOT$/Script/AtomicNET/Platform/Android/"
+            ]
+        },
+        {
+            "name": "AtomicNETService",
+            "platforms" : ["desktop"],
+            "outputType" : "Exe",
+            "rootNamespace" : "AtomicTools",
+            "assemblyName" : "AtomicNETService",
+            "assemblyOutputPath" : "..\\..\\$ATOMIC_CONFIG$\\AtomicNETService\\",
+            "projectGuid": "C74AB5E9-7517-11E6-AA19-005056C00008",
+            "references" : [
+                "System",
+                "<Reference Include=\"System.Collections.Immutable, Version=1.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\"><HintPath>..\\packages\\System.Collections.Immutable.1.2.0\\lib\\portable-net45+win8+wp8+wpa81\\System.Collections.Immutable.dll</HintPath><Private>True</Private></Reference>",
+                "System.Core" ,
+                "<Reference Include=\"System.Reflection.Metadata, Version=1.3.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\"><HintPath>..\\packages\\System.Reflection.Metadata.1.3.0\\lib\\portable-net45+win8\\System.Reflection.Metadata.dll</HintPath><Private>True</Private></Reference>",
+                "System.Xml.Linq",
+                "System.XML",
+                "AtomicNET.Desktop"
+            ],
+            "sources" : [
+                "$ATOMIC_ROOT$/Script/AtomicNET/AtomicNETService/"
+            ],
+            "packages" : [
+                "<package id=\"System.Collections.Immutable\" version=\"1.2.0\" targetFramework=\"net46\" />",
+                "<package id=\"System.Reflection.Metadata\" version=\"1.3.0\" targetFramework=\"net46\" />"
+            ]
+        }
     ]
-  }
-
-  ]
 }

+ 64 - 0
Script/AtomicNET/AtomicPlayer/AtomicPlayer.Android/MainActivity.cs

@@ -0,0 +1,64 @@
+using Android.App;
+using Android.Content.PM;
+using Android.Widget;
+using Android.OS;
+using Android.Views;
+using AtomicEngine;
+
+using AtomicBlaster;
+
+namespace AtomicPlayer
+{
+	[Activity(Label = "AtomicPlayer", MainLauncher = true,
+		Icon = "@drawable/icon", Theme = "@android:style/Theme.NoTitleBar.Fullscreen",
+		ConfigurationChanges = ConfigChanges.KeyboardHidden | ConfigChanges.Orientation,
+		ScreenOrientation = ScreenOrientation.Landscape)]
+	public class MainActivity : Activity
+	{
+		protected override void OnCreate(Bundle bundle)
+		{
+			base.OnCreate(bundle);
+			var mLayout = new AbsoluteLayout(this);
+            var surface = AndroidSDLSurface.CreateSurface(this, false, typeof(GameRoot));
+			mLayout.AddView(surface);
+			SetContentView(mLayout);
+		}
+
+		protected override void OnResume()
+		{
+            AndroidSDLSurface.OnResume();
+			base.OnResume();
+		}
+
+		protected override void OnPause()
+		{
+            AndroidSDLSurface.OnPause();
+			base.OnPause();
+		}
+
+		public override void OnLowMemory()
+		{
+            AndroidSDLSurface.OnLowMemory();
+			base.OnLowMemory();
+		}
+
+		protected override void OnDestroy()
+		{
+            AndroidSDLSurface.OnDestroy();
+			base.OnDestroy();
+		}
+
+		public override bool DispatchKeyEvent(KeyEvent e)
+		{
+			if (!AndroidSDLSurface.DispatchKeyEvent(e))
+				return false;
+			return base.DispatchKeyEvent(e);
+		}
+
+		public override void OnWindowFocusChanged(bool hasFocus)
+		{
+            AndroidSDLSurface.OnWindowFocusChanged(hasFocus);
+			base.OnWindowFocusChanged(hasFocus);
+		}
+	}
+}

+ 18 - 0
Script/AtomicNET/AtomicPlayer/AtomicPlayer.Desktop/Program.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Linq;
+
+using AtomicEngine;
+using AtomicBlaster;
+
+namespace AtomicPlayer
+{
+    public class Program
+    {        
+        public static void Main(string[] args)
+        {
+            Application.Run<GameRoot>(args);
+        }
+    }
+}

+ 0 - 20
Script/AtomicNET/AtomicPlayer/Program.cs

@@ -1,20 +0,0 @@
-
-using AtomicEngine;
-
-public class Program
-{
-    public static void Main(string[] args)
-    {
-        // Create the Application
-        var app = NETAtomicPlayer.Create(args);
-
-        // Managed code in charge of main loop
-        while (app.RunFrame())
-        {
-
-        }
-
-        // Shut 'er down
-        app.Shutdown();
-    }
-}

+ 89 - 0
Script/AtomicNET/AtomicProject.json

@@ -0,0 +1,89 @@
+{
+    "solution" : {
+        "name" : "$ATOMIC_PROJECT_NAME$",
+        "outputPath" : "$ATOMIC_PROJECT_ROOT$/AtomicNET/Solution/"
+    },
+    "projects" : [
+        {
+            "name": "$ATOMIC_PROJECT_NAME$.Shared",
+            "outputType" : "Shared",
+            "projectGuid" : "071BB14E-7518-11E6-A32D-005056C00008",
+            "sources" : [
+                "$ATOMIC_PROJECT_ROOT$/Resources/"
+            ]
+        },
+        {
+            "name": "$ATOMIC_PROJECT_NAME$",
+            "outputType" : "Library",
+            "rootNamespace" : "",
+            "assemblyName" : "$ATOMIC_PROJECT_NAME$",
+            "assemblyOutputPath" : "$ATOMIC_PROJECT_ROOT$\\AtomicNET\\$ATOMIC_CONFIG$\\Bin\\Portable",
+            "projectGuid" : "071BB14F-7518-11E6-BF97-005056C00008",
+            "projectTypeGuids" : ["786C830F-07A1-408B-BD7F-6EE04809D6DB", "FAE04EC0-301F-11D3-BF4B-00C04F79EFBC"],
+            "targetFrameworkProfile" : "Profile111",
+            "importProjects" : ["$(MSBuildExtensionsPath32)\\Microsoft\\Portable\\$(TargetFrameworkVersion)\\Microsoft.Portable.CSharp.targets"],
+            "references" : [
+                "AtomicNET"
+            ],
+            "sharedReferences" : [
+                "$ATOMIC_PROJECT_NAME$.Shared"
+            ],
+            "sources" : [
+            ]
+        },
+        {
+            "name": "$ATOMIC_PROJECT_NAME$.Desktop",
+            "platforms" : ["desktop"],
+            "outputType" : "Exe",
+            "defineConstants" : ["ATOMIC_DESKTOP"],
+            "rootNamespace" : "",
+            "assemblyName" : "$ATOMIC_PROJECT_NAME$",
+            "assemblyOutputPath" : "$ATOMIC_PROJECT_ROOT$\\AtomicNET\\$ATOMIC_CONFIG$\\Bin\\Desktop",
+            "projectGuid" : "071BB150-7518-11E6-AB1D-005056C00008",
+            "playerApplication" : true,
+            "references" : [
+                "System",
+                "System.Core" ,
+                "System.Xml.Linq",
+                "System.XML",
+                "AtomicNET.Desktop"
+            ],
+            "sharedReferences" : [
+                "$ATOMIC_PROJECT_NAME$.Shared"
+            ],
+            "sources" : [
+                "$ATOMIC_ROOT$/Script/AtomicNET/AtomicPlayer/AtomicPlayer.Common",
+                "$ATOMIC_PROJECT_ROOT$/Project/AtomicNET/Platforms/Desktop"
+            ]
+        },
+        {
+            "name": "$ATOMIC_PROJECT_NAME$.Android",
+            "platforms" : ["android"],
+            "outputType" : "Library",
+            "defineConstants" : ["ATOMIC_ANDROID"],
+            "rootNamespace" : "",
+            "assemblyName" : "$ATOMIC_PROJECT_NAME$",
+            "projectTypeGuids" : ["EFBA0AD7-5A72-4C68-AF49-83D382785DCF", "FAE04EC0-301F-11D3-BF4B-00C04F79EFBC"],
+            "projectGuid" : "071BD84E-7518-11E6-B60F-005056C00008",
+            "assemblyOutputPath" : "$ATOMIC_PROJECT_ROOT$\\AtomicNET\\$ATOMIC_CONFIG$\\Bin\\Android",
+            "androidApplication" : true,
+            "playerApplication" : true,
+            "references" : [
+                "Mono.Android",
+                "System",
+                "System.Core",
+                "System.Xml.Linq",
+                "System.Xml",
+                "AtomicNET.Android.SDL",
+                "AtomicNET.Android"
+            ],
+            "sharedReferences" : [
+                "$ATOMIC_PROJECT_NAME$.Shared"
+            ],
+            "sources" : [
+                "$ATOMIC_ROOT$/Script/AtomicNET/AtomicPlayer/AtomicPlayer.Common",
+                "$ATOMIC_PROJECT_ROOT$/Project/AtomicNET/Platforms/Android"
+            ]
+        }
+    ]
+}

+ 143 - 0
Script/AtomicNET/Platform/Android/AndroidSDLSurface.cs

@@ -0,0 +1,143 @@
+/*
+Copyright(c) 2015 Xamarin Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+using System;
+using System.Runtime.InteropServices;
+using System.Threading;
+using Android.App;
+using Android.Content;
+using Android.Util;
+using Android.Views;
+using Java.Lang;
+using Org.Libsdl.App;
+
+namespace AtomicEngine
+{
+
+    /// <summary>
+    /// A controller that provides a SDLSurface that can be used in any activity.
+    /// Make sure you handle these events in your Activity:
+    /// - OnResume
+    /// - OnPause
+    /// - OnLowMemory
+    /// - OnDestroy
+    /// - DispatchKeyEvent
+    /// - OnWindowFocusChanged
+    /// </summary>
+    public class AndroidSDLSurface : IAtomicSDLSurface
+    {
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        public delegate int SDLEntryCallback();
+
+        [DllImport(Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern void RegisterSDLEntryCallback(SDLEntryCallback callback);
+
+        public SDLSurface SdlSurface;
+
+        AndroidSDLSurface(SDLSurface sdlSurface)
+        {
+            SdlSurface = sdlSurface;
+        }
+
+        public void Remove()
+        {
+            var vg = SdlSurface?.Parent as ViewGroup;
+            if (SdlSurface != null && vg != null)
+            {
+                //vg.RemoveView(SdlSurface);
+                SdlSurface.Enabled = false;
+                SdlSurface.Visibility = ViewStates.Gone;
+            }
+        }
+
+        public bool IsAlive => SDLActivity.MIsSurfaceReady;
+
+        static Type appDelegateType;
+
+        /// <summary>
+        /// Creates a view (SurfaceView) that can be added anywhere
+        /// </summary>
+        public static SDLSurface CreateSurface(Activity activity, bool finishActivtiyOnExit = false, Type appDelegateType = null)
+        {
+            AndroidSDLSurface.appDelegateType = appDelegateType;
+            var surface = SDLActivity.CreateSurface(activity);
+            SetSDLEntryCallback(finishActivtiyOnExit, surface);
+            return surface;
+        }
+
+        public static void OnResume()
+        {
+            SDLActivity.OnResume();
+        }
+
+        public static void OnPause()
+        {
+            SDLActivity.OnPause();
+        }
+
+        public static void OnLowMemory()
+        {
+            SDLActivity.OnLowMemory();
+        }
+
+        public static void OnDestroy()
+        {
+            SDLActivity.OnDestroy();
+        }
+
+        public static bool DispatchKeyEvent(KeyEvent keyEvent)
+        {
+            return SDLActivity.DispatchKeyEvent(keyEvent);
+        }
+
+        public static void OnWindowFocusChanged(bool focus)
+        {
+            SDLActivity.OnWindowFocusChanged(focus);
+        }
+
+        /// <summary>
+        /// The simpliest way to launch a game. It opens a special full-screen activity
+        /// </summary>
+        public static void RunInActivity()
+        {
+            SetSDLEntryCallback(true, null);
+            var context = Android.App.Application.Context;
+            var intent = new Intent(context, typeof(Org.Libsdl.App.AtomicActivity));
+            intent.AddFlags(ActivityFlags.NewTask);
+            context.StartActivity(intent);
+        }
+
+        static AndroidSDLSurface __surface;
+
+        static void SetSDLEntryCallback(bool finishActivityOnExit, SDLSurface surface)
+        {
+            __surface = new AndroidSDLSurface(surface);
+
+            SDLActivity.FinishActivityOnNativeExit = finishActivityOnExit;
+
+            RegisterSDLEntryCallback( () => {
+                return Application.Run(appDelegateType, new string[0]);
+            });
+        }
+    }
+}

+ 10 - 0
Script/AtomicNET/Platform/Android/JavaSDL/.gitignore

@@ -0,0 +1,10 @@
+build/
+docs/
+.DS_Store
+*.iml
+*/*.iml
+local.properties
+.gradle/
+.idea/
+infer-out/
+example/

+ 4 - 0
Script/AtomicNET/Platform/Android/JavaSDL/Metadata.xml

@@ -0,0 +1,4 @@
+<metadata>
+	<attr path="/api/package[@name='org.libsdl.app']/class[@name='SDLJoystickHandler']" name="visibility">public</attr>
+	<attr path="/api/package[@name='org.libsdl.app']/class[@name='SDLSurface']" name="visibility">public</attr>
+</metadata>

+ 1 - 0
Script/AtomicNET/Platform/Android/JavaSDL/VERSION

@@ -0,0 +1 @@
+0.9.0

+ 59 - 0
Script/AtomicNET/Platform/Android/JavaSDL/build.gradle

@@ -0,0 +1,59 @@
+import com.android.builder.core.BuilderConstants
+
+import java.util.regex.Pattern
+
+buildscript {
+    repositories {
+        jcenter()
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:2.1.2'
+    }
+}
+
+apply plugin: 'com.android.library'
+
+repositories {
+    mavenCentral()
+}
+
+allprojects {
+    gradle.projectsEvaluated {
+        tasks.withType(JavaCompile) {
+            options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
+        }
+    }
+}
+
+def getVersionName = { ->
+    def stdout = new ByteArrayOutputStream()
+    exec {
+        commandLine 'git', 'describe', '--tags', '--always'
+        standardOutput = stdout
+    }
+    return stdout.toString().trim()
+}
+
+android {
+    // IMPORTANT: Changing versions?  Update `setup` as well!
+    compileSdkVersion 23
+    buildToolsVersion "23.0.2"
+
+    defaultPublishConfig ""
+    publishNonDefault true
+
+    defaultConfig {
+        minSdkVersion 14
+        targetSdkVersion 23
+        versionCode 2
+        versionName getVersionName()
+        consumerProguardFiles 'proguard.txt'
+        archivesBaseName = 'atomicjavasdl'
+    }
+
+    productFlavors {
+    }
+}
+
+dependencies {
+}

+ 8 - 0
Script/AtomicNET/Platform/Android/JavaSDL/compile

@@ -0,0 +1,8 @@
+#!/bin/bash
+set -euo pipefail
+IFS=$'\n\t'
+
+ANDROID_HOME=$(brew --prefix android-sdk)
+export ANDROID_HOME
+
+./gradlew assemble

BIN
Script/AtomicNET/Platform/Android/JavaSDL/gradle/wrapper/gradle-wrapper.jar


+ 6 - 0
Script/AtomicNET/Platform/Android/JavaSDL/gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,6 @@
+#Wed Aug 31 08:55:29 PDT 2016
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip

+ 160 - 0
Script/AtomicNET/Platform/Android/JavaSDL/gradlew

@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

+ 90 - 0
Script/AtomicNET/Platform/Android/JavaSDL/gradlew.bat

@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega

+ 0 - 0
Script/AtomicNET/Platform/Android/JavaSDL/proguard.txt


+ 36 - 0
Script/AtomicNET/Platform/Android/JavaSDL/release

@@ -0,0 +1,36 @@
+#!/bin/bash
+set -euo pipefail
+IFS=$'\n\t'
+
+err=0
+
+# Disallow unstaged changes in the working tree
+if ! git diff-files --quiet --ignore-submodules --
+then
+    echo >&2 "cannot $0: you have unstaged changes."
+    git diff-files --name-status -r --ignore-submodules -- >&2
+    err=1
+fi
+
+# Disallow uncommitted changes in the index
+if ! git diff-index --cached --quiet HEAD --ignore-submodules --
+then
+    echo >&2 "cannot $0: your index contains uncommitted changes."
+    git diff-index --cached --name-status -r --ignore-submodules HEAD -- >&2
+    err=1
+fi
+
+if [ $err = 1 ]
+then
+    echo >&2 "Please commit or stash them."
+    exit 1
+fi
+
+read -r line < "VERSION"
+echo "Creating Git tag: $line"
+git tag -a "$line"
+if ! [ $? -eq 0 ]; then
+    exit 1
+fi
+
+./compile

+ 0 - 0
Script/AtomicNET/Platform/Android/JavaSDL/settings.gradle


+ 30 - 0
Script/AtomicNET/Platform/Android/JavaSDL/setup

@@ -0,0 +1,30 @@
+#!/bin/bash
+set -euo pipefail
+IFS=$'\n\t'
+
+brew update
+
+# TODO: Ensure both Brew and Caskroom are set up.
+if [ "$(which java)" == "" ]; then
+  echo "ERROR: Install JDK first.  Maybe use 'brew cask install java'.  Leaving this to you in case you want to handle this differently."
+  exit 1
+fi
+
+if [ "$(which ant)" == "" ]; then brew install ant; fi
+if [ "$(which mvn)" == "" ]; then brew install maven; fi
+if [ "$(which android)" == "" ]; then
+  if [ ! -e "$(brew --prefix android-sdk)" ]; then
+    brew install android-sdk
+  else
+    brew link android-sdk
+  fi
+fi
+
+ANDROID_HOME=$(brew --prefix android-sdk)
+export ANDROID_HOME
+
+echo y | android update sdk --all --no-ui --filter 'tools,platform-tools'
+echo y | android update sdk --all --no-ui --filter 'android-23'
+echo y | android update sdk --all --no-ui --filter 'build-tools-23.0.2'
+echo y | android update sdk --all --no-ui --filter 'extra-google-m2repository'
+echo y | android update sdk --all --no-ui --filter 'extra-android-m2repository'

+ 23 - 0
Script/AtomicNET/Platform/Android/JavaSDL/src/main/AndroidManifest.xml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.atomicgameengine.atomicsdl"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-sdk
+        android:minSdkVersion="15"
+        android:targetSdkVersion="23" />
+
+    <application
+        android:allowBackup="true"
+        android:label="@string/app_name" >
+        <activity
+            android:name="org.libsdl.app.AtomicActivity"
+            android:configChanges="keyboardHidden|orientation"
+            android:label="Atomic Activity"
+            android:screenOrientation="landscape"
+            android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
+        </activity>
+    </application>
+
+</manifest>

+ 60 - 0
Script/AtomicNET/Platform/Android/JavaSDL/src/main/java/org/libsdl/app/AtomicActivity.java

@@ -0,0 +1,60 @@
+package org.libsdl.app;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.widget.AbsoluteLayout;
+
+/**
+ * Created by Egor on 13/10/15.
+ */
+public class AtomicActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        SDLSurface surface = SDLActivity.createSurface(this);
+
+        AbsoluteLayout mLayout = new AbsoluteLayout(this);
+        mLayout.addView(surface);
+        setContentView(mLayout);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        SDLActivity.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        SDLActivity.onPause();
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        SDLActivity.onDestroy();
+    }
+
+    @Override
+    public void onLowMemory() {
+        super.onLowMemory();
+        SDLActivity.onLowMemory();
+    }
+
+    @Override
+    public void onWindowFocusChanged(boolean hasFocus) {
+        super.onWindowFocusChanged(hasFocus);
+        SDLActivity.onWindowFocusChanged(hasFocus);
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        if (!SDLActivity.dispatchKeyEvent(event))
+            return false;
+
+        return super.dispatchKeyEvent(event);
+    }
+}

+ 1195 - 0
Script/AtomicNET/Platform/Android/JavaSDL/src/main/java/org/libsdl/app/SDLActivity.java

@@ -0,0 +1,1195 @@
+// Modified by Lasse Oorni and Yao Wei Tjong for Urho3D
+
+package org.libsdl.app;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.lang.reflect.Method;
+
+import android.app.*;
+import android.content.*;
+import android.text.InputType;
+import android.view.*;
+import android.view.inputmethod.BaseInputConnection;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AbsoluteLayout;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.os.*;
+import android.util.Log;
+import android.util.SparseArray;
+import android.graphics.*;
+import android.graphics.drawable.Drawable;
+import android.media.*;
+import android.hardware.*;
+import android.content.pm.ActivityInfo;
+
+/**
+ SDL Activity
+ */
+public class SDLActivity {
+    private static final String TAG = "SDL";
+
+    // Keep track of the paused state
+    public static boolean mIsPaused, mIsSurfaceReady, mHasFocus;
+    public static boolean mExitCalledFromJava;
+
+    // If we want to separate mouse and touch events.
+    //  This is only toggled in native code when a hint is set!
+    public static boolean mSeparateMouseAndTouch;
+
+    // Main components
+    protected static Activity mSingleton;
+    protected static SDLSurface mSurface;
+    protected static View mTextEdit;
+    protected static ViewGroup mLayout;
+    protected static SDLJoystickHandler mJoystickHandler;
+
+    // This is what SDL runs in. It invokes SDL_main(), eventually
+    protected static Thread mSDLThread;
+
+    // Audio
+    protected static AudioTrack mAudioTrack;
+
+    // Urho3D: flag to load the .so and a new method load them
+    private static boolean mIsSharedLibraryLoaded = false;
+
+    protected boolean onLoadLibrary(ArrayList<String> libraryNames) {
+        for (final String name : libraryNames) {
+            System.loadLibrary(name);
+        }
+        return true;
+    }
+
+    public static void initialize() {
+        // The static nature of the singleton and Android quirkyness force us to initialize everything here
+        // Otherwise, when exiting the app and returning to it, these variables *keep* their pre exit values
+        mSingleton = null;
+        mSurface = null;
+        mTextEdit = null;
+        mLayout = null;
+        mJoystickHandler = null;
+        mSDLThread = null;
+        mAudioTrack = null;
+        mExitCalledFromJava = false;
+        mIsPaused = false;
+        mIsSurfaceReady = false;
+        mHasFocus = true;
+    }
+
+    public static SDLSurface createSurface(Activity activity) {
+        SDLActivity.initialize();
+        mSingleton = activity;
+
+        if (!mIsSharedLibraryLoaded) {
+            System.loadLibrary("AtomicNETNative");
+            mIsSharedLibraryLoaded = true;
+        }
+        // Set up the surface
+        mSurface = new SDLSurface(activity.getApplication());
+
+        if(Build.VERSION.SDK_INT >= 12) {
+            mJoystickHandler = new SDLJoystickHandler_API12();
+        }
+        else {
+            mJoystickHandler = new SDLJoystickHandler();
+        }
+
+        return mSurface;
+    }
+
+    // Events
+    public static void onPause() {
+        Log.v(TAG, "onPause()");
+
+        if (!SDLActivity.mIsSharedLibraryLoaded) {  // Urho3D
+            return;
+        }
+
+        SDLActivity.handlePause();
+    }
+
+    public static void onResume() {
+        Log.v(TAG, "onResume()");
+
+        if (!SDLActivity.mIsSharedLibraryLoaded) {  // Urho3D
+            return;
+        }
+
+        SDLActivity.handleResume();
+    }
+
+
+    public static void onWindowFocusChanged(boolean hasFocus) {
+        Log.v(TAG, "onWindowFocusChanged(): " + hasFocus);
+
+        if (!SDLActivity.mIsSharedLibraryLoaded) {  // Urho3D
+            return;
+        }
+
+        SDLActivity.mHasFocus = hasFocus;
+        if (hasFocus) {
+            SDLActivity.handleResume();
+        }
+    }
+
+    public static void onLowMemory() {
+        Log.v(TAG, "onLowMemory()");
+
+        if (!SDLActivity.mIsSharedLibraryLoaded) {  // Urho3D
+            return;
+        }
+
+        SDLActivity.nativeLowMemory();
+    }
+
+    public static void onDestroy() {
+        Log.v(TAG, "onDestroy()");
+
+        if (!SDLActivity.mIsSharedLibraryLoaded) {  // Urho3D
+            // Reset everything in case the user re opens the app
+            SDLActivity.initialize();
+            return;
+        }
+
+        // Send a quit message to the application
+        SDLActivity.mExitCalledFromJava = true;
+        SDLActivity.nativeQuit();
+
+        // Now wait for the SDL thread to quit
+        if (SDLActivity.mSDLThread != null) {
+            try {
+                SDLActivity.mSDLThread.join();
+            } catch(Exception e) {
+                Log.v(TAG, "Problem stopping thread: " + e);
+            }
+            SDLActivity.mSDLThread = null;
+
+            //Log.v(TAG, "Finished waiting for SDL thread");
+        }
+
+        // Reset everything in case the user re opens the app
+        SDLActivity.initialize();
+    }
+
+    public static boolean dispatchKeyEvent(KeyEvent event) {
+
+        if (!SDLActivity.mIsSharedLibraryLoaded) {  // Urho3D
+            return false;
+        }
+
+        int keyCode = event.getKeyCode();
+        // Ignore certain special keys so they're handled by Android
+        // Urho3D: also ignore the Home key
+        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
+                keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
+                keyCode == KeyEvent.KEYCODE_HOME ||
+                keyCode == KeyEvent.KEYCODE_CAMERA ||
+                keyCode == 168 || /* API 11: KeyEvent.KEYCODE_ZOOM_IN */
+                keyCode == 169 /* API 11: KeyEvent.KEYCODE_ZOOM_OUT */
+                ) {
+            return false;
+        }
+        return true;
+    }
+
+    /** Called by onPause or surfaceDestroyed. Even if surfaceDestroyed
+     *  is the first to be called, mIsSurfaceReady should still be set
+     *  to 'true' during the call to onPause (in a usual scenario).
+     */
+    public static void handlePause() {
+        if (!SDLActivity.mIsPaused && SDLActivity.mIsSurfaceReady) {
+            SDLActivity.mIsPaused = true;
+            SDLActivity.nativePause();
+            mSurface.handlePause();
+        }
+    }
+
+    /** Called by onResume or surfaceCreated. An actual resume should be done only when the surface is ready.
+     * Note: Some Android variants may send multiple surfaceChanged events, so we don't need to resume
+     * every time we get one of those events, only if it comes after surfaceDestroyed
+     */
+    public static void handleResume() {
+        if (SDLActivity.mIsPaused && SDLActivity.mIsSurfaceReady && SDLActivity.mHasFocus) {
+            SDLActivity.mIsPaused = false;
+            SDLActivity.nativeResume();
+            mSurface.handleResume();
+        }
+    }
+    /* The native thread has finished */
+    public static void handleNativeExit() throws Exception {
+        SDLActivity.mSDLThread = null;
+        Log.d(TAG, "handleNativeExit");
+        if (mSingleton != null && FinishActivityOnNativeExit){
+            mSingleton.finish();
+        }
+    }
+
+    public static boolean FinishActivityOnNativeExit = true;
+
+    // Messages from the SDLMain thread
+    static final int COMMAND_CHANGE_TITLE = 1;
+    static final int COMMAND_UNUSED = 2;
+    static final int COMMAND_TEXTEDIT_HIDE = 3;
+    static final int COMMAND_SET_KEEP_SCREEN_ON = 5;
+
+    protected static final int COMMAND_USER = 0x8000;
+
+    /**
+     * This method is called by SDL if SDL did not handle a message itself.
+     * This happens if a received message contains an unsupported command.
+     * Method can be overwritten to handle Messages in a different class.
+     * @param command the command of the message.
+     * @param param the parameter of the message. May be null.
+     * @return if the message was handled in overridden method.
+     */
+    protected boolean onUnhandledMessage(int command, Object param) {
+        return false;
+    }
+
+    /**
+     * A Handler class for Messages from native SDL applications.
+     * It uses current Activities as target (e.g. for the title).
+     * static to prevent implicit references to enclosing object.
+     */
+    protected static class SDLCommandHandler extends Handler {
+        @Override
+        public void handleMessage(Message msg) {
+            Context context = getContext();
+            if (context == null) {
+                Log.e(TAG, "error handling message, getContext() returned null");
+                return;
+            }
+            switch (msg.arg1) {
+                case COMMAND_CHANGE_TITLE:
+                    if (context instanceof Activity) {
+                        ((Activity) context).setTitle((String)msg.obj);
+                    } else {
+                        Log.e(TAG, "error handling message, getContext() returned no Activity");
+                    }
+                    break;
+                case COMMAND_TEXTEDIT_HIDE:
+                    if (mTextEdit != null) {
+                        mTextEdit.setVisibility(View.GONE);
+
+                        InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
+                        imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0);
+                    }
+                    break;
+                case COMMAND_SET_KEEP_SCREEN_ON:
+                {
+                    Window window = ((Activity) context).getWindow();
+                    if (window != null) {
+                        if ((msg.obj instanceof Integer) && (((Integer) msg.obj).intValue() != 0)) {
+                            window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+                        } else {
+                            window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+                        }
+                    }
+                    break;
+                }
+                default:
+                    Log.e(TAG, "error handling message, command is " + msg.arg1);
+            }
+        }
+    }
+
+    // Handler for the messages
+    static Handler commandHandler = new SDLCommandHandler();
+
+    // Send a message from the SDLMain thread
+    public static boolean sendCommand(int command, Object data) {
+        Message msg = commandHandler.obtainMessage();
+        msg.arg1 = command;
+        msg.obj = data;
+        return commandHandler.sendMessage(msg);
+    }
+
+    // C functions we call
+    // Urho3D: added parameter
+    public static native int nativeInit(Object arguments, String filesDir);
+    public static native void nativeLowMemory();
+    public static native void nativeQuit();
+    public static native void nativePause();
+    public static native void nativeResume();
+    public static native void onNativeDropFile(String filename);
+    public static native void onNativeResize(int x, int y, int format, float rate);
+    public static native int onNativePadDown(int device_id, int keycode);
+    public static native int onNativePadUp(int device_id, int keycode);
+    public static native void onNativeJoy(int device_id, int axis,
+                                          float value);
+    public static native void onNativeHat(int device_id, int hat_id,
+                                          int x, int y);
+    public static native void onNativeKeyDown(int keycode);
+    public static native void onNativeKeyUp(int keycode);
+    public static native void onNativeKeyboardFocusLost();
+    public static native void onNativeMouse(int button, int action, float x, float y);
+    public static native void onNativeTouch(int touchDevId, int pointerFingerId,
+                                            int action, float x,
+                                            float y, float p);
+    public static native void onNativeAccel(float x, float y, float z);
+    public static native void onNativeSurfaceChanged();
+    public static native void onNativeSurfaceDestroyed();
+    public static native int nativeAddJoystick(int device_id, String name,
+                                               int is_accelerometer, int nbuttons,
+                                               int naxes, int nhats, int nballs);
+    public static native int nativeRemoveJoystick(int device_id);
+    public static native String nativeGetHint(String name);
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean setActivityTitle(String title) {
+        // Called from SDLMain() thread and can't directly affect the view
+        return SDLActivity.sendCommand(COMMAND_CHANGE_TITLE, title);
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean sendMessage(int command, int param) {
+        return SDLActivity.sendCommand(command, Integer.valueOf(param));
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static Context getContext() {
+        return mSingleton;
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     * @return result of getSystemService(name) but executed on UI thread.
+     */
+    public Object getSystemServiceFromUiThread(final String name) {
+        final Object lock = new Object();
+        final Object[] results = new Object[2]; // array for writable variables
+        synchronized (lock) {
+            mSingleton.runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    synchronized (lock) {
+                        results[0] = mSingleton.getSystemService(name);
+                        results[1] = Boolean.TRUE;
+                        lock.notify();
+                    }
+                }
+            });
+            if (results[1] == null) {
+                try {
+                    lock.wait();
+                } catch (InterruptedException ex) {
+                    ex.printStackTrace();
+                }
+            }
+        }
+        return results[0];
+    }
+
+    static class ShowTextInputTask implements Runnable {
+        /*
+         * This is used to regulate the pan&scan method to have some offset from
+         * the bottom edge of the input region and the top edge of an input
+         * method (soft keyboard)
+         */
+        static final int HEIGHT_PADDING = 15;
+
+        public int x, y, w, h;
+
+        public ShowTextInputTask(int x, int y, int w, int h) {
+            this.x = x;
+            this.y = y;
+            this.w = w;
+            this.h = h;
+        }
+
+        @Override
+        public void run() {
+            AbsoluteLayout.LayoutParams params = new AbsoluteLayout.LayoutParams(
+                    w, h + HEIGHT_PADDING, x, y);
+
+            if (mTextEdit == null) {
+                ViewParent parent = mSurface.getParent();
+                if (parent instanceof ViewGroup){
+                    mTextEdit = new DummyEdit(getContext());
+                    ((ViewGroup)parent).addView(mTextEdit, params);
+                } else {
+                    return;
+                }
+            } else {
+                mTextEdit.setLayoutParams(params);
+            }
+
+            mTextEdit.setVisibility(View.VISIBLE);
+            mTextEdit.requestFocus();
+
+            InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+            imm.showSoftInput(mTextEdit, 0);
+        }
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static boolean showTextInput(int x, int y, int w, int h) {
+        // Transfer the task to the main thread as a Runnable
+        return SDLActivity.commandHandler.post(new ShowTextInputTask(x, y, w, h));
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static Surface getNativeSurface() {
+        return SDLActivity.mSurface.getNativeSurface();
+    }
+
+    // Audio
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static int audioInit(int sampleRate, boolean is16Bit, boolean isStereo, int desiredFrames) {
+        int channelConfig = isStereo ? AudioFormat.CHANNEL_CONFIGURATION_STEREO : AudioFormat.CHANNEL_CONFIGURATION_MONO;
+        int audioFormat = is16Bit ? AudioFormat.ENCODING_PCM_16BIT : AudioFormat.ENCODING_PCM_8BIT;
+        int frameSize = (isStereo ? 2 : 1) * (is16Bit ? 2 : 1);
+
+        Log.v(TAG, "SDL audio: wanted " + (isStereo ? "stereo" : "mono") + " " + (is16Bit ? "16-bit" : "8-bit") + " " + (sampleRate / 1000f) + "kHz, " + desiredFrames + " frames buffer");
+
+        // Let the user pick a larger buffer if they really want -- but ye
+        // gods they probably shouldn't, the minimums are horrifyingly high
+        // latency already
+        desiredFrames = Math.max(desiredFrames, (AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat) + frameSize - 1) / frameSize);
+
+        if (mAudioTrack == null) {
+            mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
+                    channelConfig, audioFormat, desiredFrames * frameSize, AudioTrack.MODE_STREAM);
+
+            // Instantiating AudioTrack can "succeed" without an exception and the track may still be invalid
+            // Ref: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/media/java/android/media/AudioTrack.java
+            // Ref: http://developer.android.com/reference/android/media/AudioTrack.html#getState()
+
+            if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
+                Log.e(TAG, "Failed during initialization of Audio Track");
+                mAudioTrack = null;
+                return -1;
+            }
+
+            mAudioTrack.play();
+        }
+
+        Log.v(TAG, "SDL audio: got " + ((mAudioTrack.getChannelCount() >= 2) ? "stereo" : "mono") + " " + ((mAudioTrack.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) ? "16-bit" : "8-bit") + " " + (mAudioTrack.getSampleRate() / 1000f) + "kHz, " + desiredFrames + " frames buffer");
+
+        return 0;
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static void audioWriteShortBuffer(short[] buffer) {
+        for (int i = 0; i < buffer.length; ) {
+            int result = mAudioTrack.write(buffer, i, buffer.length - i);
+            if (result > 0) {
+                i += result;
+            } else if (result == 0) {
+                try {
+                    Thread.sleep(1);
+                } catch(InterruptedException e) {
+                    // Nom nom
+                }
+            } else {
+                Log.w(TAG, "SDL audio: error return from write(short)");
+                return;
+            }
+        }
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static void audioWriteByteBuffer(byte[] buffer) {
+        for (int i = 0; i < buffer.length; ) {
+            int result = mAudioTrack.write(buffer, i, buffer.length - i);
+            if (result > 0) {
+                i += result;
+            } else if (result == 0) {
+                try {
+                    Thread.sleep(1);
+                } catch(InterruptedException e) {
+                    // Nom nom
+                }
+            } else {
+                Log.w(TAG, "SDL audio: error return from write(byte)");
+                return;
+            }
+        }
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static void audioQuit() {
+        if (mAudioTrack != null) {
+            mAudioTrack.stop();
+            mAudioTrack = null;
+        }
+    }
+
+    // Input
+
+    /**
+     * This method is called by SDL using JNI.
+     * @return an array which may be empty but is never null.
+     */
+    public static int[] inputGetInputDeviceIds(int sources) {
+        int[] ids = InputDevice.getDeviceIds();
+        int[] filtered = new int[ids.length];
+        int used = 0;
+        for (int i = 0; i < ids.length; ++i) {
+            InputDevice device = InputDevice.getDevice(ids[i]);
+            if ((device != null) && ((device.getSources() & sources) != 0)) {
+                filtered[used++] = device.getId();
+            }
+        }
+        return Arrays.copyOf(filtered, used);
+    }
+
+    // Urho3D: add handler null check
+    // Joystick glue code, just a series of stubs that redirect to the SDLJoystickHandler instance
+    public static boolean handleJoystickMotionEvent(MotionEvent event) {
+        return mJoystickHandler != null && mJoystickHandler.handleMotionEvent(event);
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     */
+    public static void pollInputDevices() {
+        if (SDLActivity.mSDLThread != null && mJoystickHandler != null) {
+            mJoystickHandler.pollInputDevices();
+        }
+    }
+
+    // APK expansion files support
+
+    /** com.android.vending.expansion.zipfile.ZipResourceFile object or null. */
+    private static Object expansionFile;
+
+    /** com.android.vending.expansion.zipfile.ZipResourceFile's getInputStream() or null. */
+    private static Method expansionFileMethod;
+
+    /**
+     * This method was called by SDL using JNI.
+     * @deprecated because of an incorrect name
+     */
+    @Deprecated
+    public InputStream openAPKExtensionInputStream(String fileName) throws IOException {
+        return openAPKExpansionInputStream(fileName);
+    }
+
+    /**
+     * This method is called by SDL using JNI.
+     * @return an InputStream on success or null if no expansion file was used.
+     * @throws IOException on errors. Message is set for the SDL error message.
+     */
+    public static InputStream openAPKExpansionInputStream(String fileName) throws IOException {
+        // Get a ZipResourceFile representing a merger of both the main and patch files
+        if (expansionFile == null) {
+            String mainHint = nativeGetHint("SDL_ANDROID_APK_EXPANSION_MAIN_FILE_VERSION");
+            if (mainHint == null) {
+                return null; // no expansion use if no main version was set
+            }
+            String patchHint = nativeGetHint("SDL_ANDROID_APK_EXPANSION_PATCH_FILE_VERSION");
+            if (patchHint == null) {
+                return null; // no expansion use if no patch version was set
+            }
+
+            Integer mainVersion;
+            Integer patchVersion;
+            try {
+                mainVersion = Integer.valueOf(mainHint);
+                patchVersion = Integer.valueOf(patchHint);
+            } catch (NumberFormatException ex) {
+                ex.printStackTrace();
+                throw new IOException("No valid file versions set for APK expansion files", ex);
+            }
+
+            try {
+                // To avoid direct dependency on Google APK expansion library that is
+                // not a part of Android SDK we access it using reflection
+                expansionFile = Class.forName("com.android.vending.expansion.zipfile.APKExpansionSupport")
+                        .getMethod("getAPKExpansionZipFile", Context.class, int.class, int.class)
+                        .invoke(null, mSingleton, mainVersion, patchVersion);
+
+                expansionFileMethod = expansionFile.getClass()
+                        .getMethod("getInputStream", String.class);
+            } catch (Exception ex) {
+                ex.printStackTrace();
+                expansionFile = null;
+                expansionFileMethod = null;
+                throw new IOException("Could not access APK expansion support library", ex);
+            }
+        }
+
+        // Get an input stream for a known file inside the expansion file ZIPs
+        InputStream fileStream;
+        try {
+            fileStream = (InputStream)expansionFileMethod.invoke(expansionFile, fileName);
+        } catch (Exception ex) {
+            // calling "getInputStream" failed
+            ex.printStackTrace();
+            throw new IOException("Could not open stream from APK expansion file", ex);
+        }
+
+        if (fileStream == null) {
+            // calling "getInputStream" was successful but null was returned
+            throw new IOException("Could not find path in APK expansion file");
+        }
+
+        return fileStream;
+    }
+
+    // Messagebox
+
+    /** Result of current messagebox. Also used for blocking the calling thread. */
+    protected final int[] messageboxSelection = new int[1];
+
+    /** Id of current dialog. */
+    protected int dialogs = 0;
+
+    /**
+     * This method is called by SDL using JNI.
+     * Shows the messagebox from UI thread and block calling thread.
+     * buttonFlags, buttonIds and buttonTexts must have same length.
+     * @param buttonFlags array containing flags for every button.
+     * @param buttonIds array containing id for every button.
+     * @param buttonTexts array containing text for every button.
+     * @param colors null for default or array of length 5 containing colors.
+     * @return button id or -1.
+     */
+    public int messageboxShowMessageBox(
+            final int flags,
+            final String title,
+            final String message,
+            final int[] buttonFlags,
+            final int[] buttonIds,
+            final String[] buttonTexts,
+            final int[] colors) {
+
+        messageboxSelection[0] = -1;
+
+        // sanity checks
+
+        if ((buttonFlags.length != buttonIds.length) && (buttonIds.length != buttonTexts.length)) {
+            return -1; // implementation broken
+        }
+
+        // collect arguments for Dialog
+
+        final Bundle args = new Bundle();
+        args.putInt("flags", flags);
+        args.putString("title", title);
+        args.putString("message", message);
+        args.putIntArray("buttonFlags", buttonFlags);
+        args.putIntArray("buttonIds", buttonIds);
+        args.putStringArray("buttonTexts", buttonTexts);
+        args.putIntArray("colors", colors);
+
+        // trigger Dialog creation on UI thread
+
+        mSingleton.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mSingleton.showDialog(dialogs++, args);
+            }
+        });
+
+        // block the calling thread
+
+        synchronized (messageboxSelection) {
+            try {
+                messageboxSelection.wait();
+            } catch (InterruptedException ex) {
+                ex.printStackTrace();
+                return -1;
+            }
+        }
+
+        // return selected value
+
+        return messageboxSelection[0];
+    }
+
+    protected Dialog onCreateDialog(int ignore, Bundle args) {
+
+        // TODO set values from "flags" to messagebox dialog
+
+        // get colors
+
+        int[] colors = args.getIntArray("colors");
+        int backgroundColor;
+        int textColor;
+        int buttonBorderColor;
+        int buttonBackgroundColor;
+        int buttonSelectedColor;
+        if (colors != null) {
+            int i = -1;
+            backgroundColor = colors[++i];
+            textColor = colors[++i];
+            buttonBorderColor = colors[++i];
+            buttonBackgroundColor = colors[++i];
+            buttonSelectedColor = colors[++i];
+        } else {
+            backgroundColor = Color.TRANSPARENT;
+            textColor = Color.TRANSPARENT;
+            buttonBorderColor = Color.TRANSPARENT;
+            buttonBackgroundColor = Color.TRANSPARENT;
+            buttonSelectedColor = Color.TRANSPARENT;
+        }
+
+        // create dialog with title and a listener to wake up calling thread
+
+        final Dialog dialog = new Dialog(mSingleton);
+        dialog.setTitle(args.getString("title"));
+        dialog.setCancelable(false);
+        dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
+            @Override
+            public void onDismiss(DialogInterface unused) {
+                synchronized (messageboxSelection) {
+                    messageboxSelection.notify();
+                }
+            }
+        });
+
+        // create text
+
+        TextView message = new TextView(mSingleton);
+        message.setGravity(Gravity.CENTER);
+        message.setText(args.getString("message"));
+        if (textColor != Color.TRANSPARENT) {
+            message.setTextColor(textColor);
+        }
+
+        // create buttons
+
+        int[] buttonFlags = args.getIntArray("buttonFlags");
+        int[] buttonIds = args.getIntArray("buttonIds");
+        String[] buttonTexts = args.getStringArray("buttonTexts");
+
+        final SparseArray<Button> mapping = new SparseArray<Button>();
+
+        LinearLayout buttons = new LinearLayout(mSingleton);
+        buttons.setOrientation(LinearLayout.HORIZONTAL);
+        buttons.setGravity(Gravity.CENTER);
+        for (int i = 0; i < buttonTexts.length; ++i) {
+            Button button = new Button(mSingleton);
+            final int id = buttonIds[i];
+            button.setOnClickListener(new View.OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    messageboxSelection[0] = id;
+                    dialog.dismiss();
+                }
+            });
+            if (buttonFlags[i] != 0) {
+                // see SDL_messagebox.h
+                if ((buttonFlags[i] & 0x00000001) != 0) {
+                    mapping.put(KeyEvent.KEYCODE_ENTER, button);
+                }
+                if ((buttonFlags[i] & 0x00000002) != 0) {
+                    mapping.put(111, button); /* API 11: KeyEvent.KEYCODE_ESCAPE */
+                }
+            }
+            button.setText(buttonTexts[i]);
+            if (textColor != Color.TRANSPARENT) {
+                button.setTextColor(textColor);
+            }
+            if (buttonBorderColor != Color.TRANSPARENT) {
+                // TODO set color for border of messagebox button
+            }
+            if (buttonBackgroundColor != Color.TRANSPARENT) {
+                Drawable drawable = button.getBackground();
+                if (drawable == null) {
+                    // setting the color this way removes the style
+                    button.setBackgroundColor(buttonBackgroundColor);
+                } else {
+                    // setting the color this way keeps the style (gradient, padding, etc.)
+                    drawable.setColorFilter(buttonBackgroundColor, PorterDuff.Mode.MULTIPLY);
+                }
+            }
+            if (buttonSelectedColor != Color.TRANSPARENT) {
+                // TODO set color for selected messagebox button
+            }
+            buttons.addView(button);
+        }
+
+        // create content
+
+        LinearLayout content = new LinearLayout(mSingleton);
+        content.setOrientation(LinearLayout.VERTICAL);
+        content.addView(message);
+        content.addView(buttons);
+        if (backgroundColor != Color.TRANSPARENT) {
+            content.setBackgroundColor(backgroundColor);
+        }
+
+        // add content to dialog and return
+
+        dialog.setContentView(content);
+        dialog.setOnKeyListener(new Dialog.OnKeyListener() {
+            @Override
+            public boolean onKey(DialogInterface d, int keyCode, KeyEvent event) {
+                Button button = mapping.get(keyCode);
+                if (button != null) {
+                    if (event.getAction() == KeyEvent.ACTION_UP) {
+                        button.performClick();
+                    }
+                    return true; // also for ignored actions
+                }
+                return false;
+            }
+        });
+
+        return dialog;
+    }
+}
+
+/**
+ Simple nativeInit() runnable
+ */
+class SDLMain implements Runnable {
+    @Override
+    public void run() {
+        // Runs SDL_main()
+        // Urho3D: pass filesDir
+        String path = ((Activity)SDLActivity.getContext()).getFilesDir().getAbsolutePath();
+        Log.v("SDLMain", "Init with path: " + path);
+        SDLActivity.nativeInit(new String[]{"app_process"}, path);
+    }
+}
+
+
+/* This is a fake invisible editor view that receives the input and defines the
+ * pan&scan region
+ */
+class DummyEdit extends View implements View.OnKeyListener {
+    InputConnection ic;
+
+    public DummyEdit(Context context) {
+        super(context);
+        setFocusableInTouchMode(true);
+        setFocusable(true);
+        setOnKeyListener(this);
+    }
+
+    @Override
+    public boolean onCheckIsTextEditor() {
+        return true;
+    }
+
+    @Override
+    public boolean onKey(View v, int keyCode, KeyEvent event) {
+
+        // This handles the hardware keyboard input
+        // Urho3D: unite input with other platforms
+        if (event.getAction() == KeyEvent.ACTION_DOWN) {
+            SDLActivity.onNativeKeyDown(keyCode);
+            ic.commitText(String.valueOf((char) event.getUnicodeChar()), 1);
+        } else if (event.getAction() == KeyEvent.ACTION_UP) {
+            SDLActivity.onNativeKeyUp(keyCode);
+        }
+
+        return true;
+    }
+
+    //
+    @Override
+    public boolean onKeyPreIme (int keyCode, KeyEvent event) {
+        // As seen on StackOverflow: http://stackoverflow.com/questions/7634346/keyboard-hide-event
+        // FIXME: Discussion at http://bugzilla.libsdl.org/show_bug.cgi?id=1639
+        // FIXME: This is not a 100% effective solution to the problem of detecting if the keyboard is showing or not
+        // FIXME: A more effective solution would be to change our Layout from AbsoluteLayout to Relative or Linear
+        // FIXME: And determine the keyboard presence doing this: http://stackoverflow.com/questions/2150078/how-to-check-visibility-of-software-keyboard-in-android
+        // FIXME: An even more effective way would be if Android provided this out of the box, but where would the fun be in that :)
+        if (event.getAction()==KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
+            if (SDLActivity.mTextEdit != null && SDLActivity.mTextEdit.getVisibility() == View.VISIBLE) {
+                SDLActivity.onNativeKeyboardFocusLost();
+            }
+        }
+        return super.onKeyPreIme(keyCode, event);
+    }
+
+    @Override
+    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+        ic = new SDLInputConnection(this, true);
+
+        outAttrs.inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;
+        outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
+                | 33554432 /* API 11: EditorInfo.IME_FLAG_NO_FULLSCREEN */;
+
+        return ic;
+    }
+}
+
+class SDLInputConnection extends BaseInputConnection {
+
+    public SDLInputConnection(View targetView, boolean fullEditor) {
+        super(targetView, fullEditor);
+
+    }
+
+    @Override
+    public boolean sendKeyEvent(KeyEvent event) {
+
+        /*
+         * This handles the keycodes from soft keyboard (and IME-translated
+         * input from hardkeyboard)
+         */
+        int keyCode = event.getKeyCode();
+        if (event.getAction() == KeyEvent.ACTION_DOWN) {
+            if (event.isPrintingKey()) {
+                commitText(String.valueOf((char) event.getUnicodeChar()), 1);
+            }
+            SDLActivity.onNativeKeyDown(keyCode);
+            return true;
+        } else if (event.getAction() == KeyEvent.ACTION_UP) {
+
+            SDLActivity.onNativeKeyUp(keyCode);
+            return true;
+        }
+        return super.sendKeyEvent(event);
+    }
+
+    @Override
+    public boolean commitText(CharSequence text, int newCursorPosition) {
+
+        nativeCommitText(text.toString(), newCursorPosition);
+
+        return super.commitText(text, newCursorPosition);
+    }
+
+    @Override
+    public boolean setComposingText(CharSequence text, int newCursorPosition) {
+
+        nativeSetComposingText(text.toString(), newCursorPosition);
+
+        return super.setComposingText(text, newCursorPosition);
+    }
+
+    public native void nativeCommitText(String text, int newCursorPosition);
+
+    public native void nativeSetComposingText(String text, int newCursorPosition);
+
+    @Override
+    public boolean deleteSurroundingText(int beforeLength, int afterLength) {
+        // Workaround to capture backspace key. Ref: http://stackoverflow.com/questions/14560344/android-backspace-in-webview-baseinputconnection
+        if (beforeLength == 1 && afterLength == 0) {
+            // backspace
+            return super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
+                    && super.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
+        }
+
+        return super.deleteSurroundingText(beforeLength, afterLength);
+    }
+}
+
+/* A null joystick handler for API level < 12 devices (the accelerometer is handled separately) */
+class SDLJoystickHandler {
+
+    /**
+     * Handles given MotionEvent.
+     * @param event the event to be handled.
+     * @return if given event was processed.
+     */
+    public boolean handleMotionEvent(MotionEvent event) {
+        return false;
+    }
+
+    /**
+     * Handles adding and removing of input devices.
+     */
+    public void pollInputDevices() {
+    }
+}
+
+/* Actual joystick functionality available for API >= 12 devices */
+class SDLJoystickHandler_API12 extends SDLJoystickHandler {
+
+    static class SDLJoystick {
+        public int device_id;
+        public String name;
+        public ArrayList<InputDevice.MotionRange> axes;
+        public ArrayList<InputDevice.MotionRange> hats;
+    }
+    static class RangeComparator implements Comparator<InputDevice.MotionRange> {
+        @Override
+        public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
+            return arg0.getAxis() - arg1.getAxis();
+        }
+    }
+
+    private ArrayList<SDLJoystick> mJoysticks;
+
+    public SDLJoystickHandler_API12() {
+
+        mJoysticks = new ArrayList<SDLJoystick>();
+    }
+
+    @Override
+    public void pollInputDevices() {
+        int[] deviceIds = InputDevice.getDeviceIds();
+        // It helps processing the device ids in reverse order
+        // For example, in the case of the XBox 360 wireless dongle,
+        // so the first controller seen by SDL matches what the receiver
+        // considers to be the first controller
+
+        for(int i=deviceIds.length-1; i>-1; i--) {
+            SDLJoystick joystick = getJoystick(deviceIds[i]);
+            if (joystick == null) {
+                joystick = new SDLJoystick();
+                InputDevice joystickDevice = InputDevice.getDevice(deviceIds[i]);
+
+                // Urho3D - revert back commit 34a0b0478654e8dfaf111aecc0e4535875c4ec87 as it does more harm than good
+                if( (joystickDevice.getSources() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
+                    joystick.device_id = deviceIds[i];
+                    joystick.name = joystickDevice.getName();
+                    joystick.axes = new ArrayList<InputDevice.MotionRange>();
+                    joystick.hats = new ArrayList<InputDevice.MotionRange>();
+
+                    List<InputDevice.MotionRange> ranges = joystickDevice.getMotionRanges();
+                    Collections.sort(ranges, new RangeComparator());
+                    for (InputDevice.MotionRange range : ranges ) {
+                        if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 ) {
+                            if (range.getAxis() == MotionEvent.AXIS_HAT_X ||
+                                    range.getAxis() == MotionEvent.AXIS_HAT_Y) {
+                                joystick.hats.add(range);
+                            }
+                            else {
+                                joystick.axes.add(range);
+                            }
+                        }
+                    }
+
+                    mJoysticks.add(joystick);
+                    SDLActivity.nativeAddJoystick(joystick.device_id, joystick.name, 0, -1,
+                            joystick.axes.size(), joystick.hats.size()/2, 0);
+                }
+            }
+        }
+
+        /* Check removed devices */
+        ArrayList<Integer> removedDevices = new ArrayList<Integer>();
+        for(int i=0; i < mJoysticks.size(); i++) {
+            int device_id = mJoysticks.get(i).device_id;
+            int j;
+            for (j=0; j < deviceIds.length; j++) {
+                if (device_id == deviceIds[j]) break;
+            }
+            if (j == deviceIds.length) {
+                removedDevices.add(Integer.valueOf(device_id));
+            }
+        }
+
+        for(int i=0; i < removedDevices.size(); i++) {
+            int device_id = removedDevices.get(i).intValue();
+            SDLActivity.nativeRemoveJoystick(device_id);
+            for (int j=0; j < mJoysticks.size(); j++) {
+                if (mJoysticks.get(j).device_id == device_id) {
+                    mJoysticks.remove(j);
+                    break;
+                }
+            }
+        }
+    }
+
+    protected SDLJoystick getJoystick(int device_id) {
+        for(int i=0; i < mJoysticks.size(); i++) {
+            if (mJoysticks.get(i).device_id == device_id) {
+                return mJoysticks.get(i);
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public boolean handleMotionEvent(MotionEvent event) {
+        if ( (event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0) {
+            int actionPointerIndex = event.getActionIndex();
+            int action = event.getActionMasked();
+            switch(action) {
+                case MotionEvent.ACTION_MOVE:
+                    SDLJoystick joystick = getJoystick(event.getDeviceId());
+                    if ( joystick != null ) {
+                        for (int i = 0; i < joystick.axes.size(); i++) {
+                            InputDevice.MotionRange range = joystick.axes.get(i);
+                            /* Normalize the value to -1...1 */
+                            float value = ( event.getAxisValue( range.getAxis(), actionPointerIndex) - range.getMin() ) / range.getRange() * 2.0f - 1.0f;
+                            SDLActivity.onNativeJoy(joystick.device_id, i, value );
+                        }
+                        for (int i = 0; i < joystick.hats.size(); i+=2) {
+                            int hatX = Math.round(event.getAxisValue( joystick.hats.get(i).getAxis(), actionPointerIndex ) );
+                            int hatY = Math.round(event.getAxisValue( joystick.hats.get(i+1).getAxis(), actionPointerIndex ) );
+                            SDLActivity.onNativeHat(joystick.device_id, i/2, hatX, hatY );
+                        }
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
+        return true;
+    }
+}
+
+class SDLGenericMotionListener_API12 implements View.OnGenericMotionListener {
+    // Generic Motion (mouse hover, joystick...) events go here
+    @Override
+    public boolean onGenericMotion(View v, MotionEvent event) {
+        float x, y;
+        int action;
+
+        switch ( event.getSource() ) {
+            case InputDevice.SOURCE_JOYSTICK:
+            case InputDevice.SOURCE_GAMEPAD:
+            case InputDevice.SOURCE_DPAD:
+                SDLActivity.handleJoystickMotionEvent(event);
+                return true;
+
+            case InputDevice.SOURCE_MOUSE:
+                action = event.getActionMasked();
+                switch (action) {
+                    case MotionEvent.ACTION_SCROLL:
+                        x = event.getAxisValue(MotionEvent.AXIS_HSCROLL, 0);
+                        y = event.getAxisValue(MotionEvent.AXIS_VSCROLL, 0);
+                        SDLActivity.onNativeMouse(0, action, x, y);
+                        return true;
+
+                    case MotionEvent.ACTION_HOVER_MOVE:
+                        x = event.getX(0);
+                        y = event.getY(0);
+
+                        SDLActivity.onNativeMouse(0, action, x, y);
+                        return true;
+
+                    default:
+                        break;
+                }
+                break;
+
+            default:
+                break;
+        }
+
+        // Event was not managed
+        return false;
+    }
+}

+ 397 - 0
Script/AtomicNET/Platform/Android/JavaSDL/src/main/java/org/libsdl/app/SDLSurface.java

@@ -0,0 +1,397 @@
+package org.libsdl.app;
+
+import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.graphics.PixelFormat;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Build;
+import android.util.Log;
+import android.view.Display;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.WindowManager;
+
+/**
+ SDLSurface. This is what we draw on, so we need to know when it's created
+ in order to do anything useful.
+
+ Because of this, that's where we set up the SDL thread
+ */
+public class SDLSurface extends SurfaceView implements SurfaceHolder.Callback,
+        View.OnKeyListener, View.OnTouchListener, SensorEventListener {
+
+    // Sensors
+    protected static SensorManager mSensorManager;
+    protected static Display mDisplay;
+
+    // Keep track of the surface size to normalize touch events
+    protected static float mWidth, mHeight;
+
+    // Startup
+    public SDLSurface(Context context) {
+        super(context);
+        getHolder().addCallback(this);
+
+        setFocusable(true);
+        setFocusableInTouchMode(true);
+        requestFocus();
+        setOnKeyListener(this);
+        setOnTouchListener(this);
+
+        mDisplay = ((WindowManager)context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
+        mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
+
+        if(Build.VERSION.SDK_INT >= 12) {
+            setOnGenericMotionListener(new SDLGenericMotionListener_API12());
+        }
+
+        // Some arbitrary defaults to avoid a potential division by zero
+        mWidth = 1.0f;
+        mHeight = 1.0f;
+    }
+
+    public void handlePause() {
+        enableSensor(Sensor.TYPE_ACCELEROMETER, false);
+    }
+
+    public void handleResume() {
+        setFocusable(true);
+        setFocusableInTouchMode(true);
+        requestFocus();
+        setOnKeyListener(this);
+        setOnTouchListener(this);
+        enableSensor(Sensor.TYPE_ACCELEROMETER, true);
+    }
+
+    public Surface getNativeSurface() {
+        return getHolder().getSurface();
+    }
+
+    // Called when we have a valid drawing surface
+    @Override
+    public void surfaceCreated(SurfaceHolder holder) {
+        Log.v("SDL", "surfaceCreated()");
+        holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
+    }
+
+    // Called when we lose the surface
+    @Override
+    public void surfaceDestroyed(SurfaceHolder holder) {
+        Log.v("SDL", "surfaceDestroyed()");
+        // Call this *before* setting mIsSurfaceReady to 'false'
+        SDLActivity.handlePause();
+        SDLActivity.mIsSurfaceReady = false;
+        SDLActivity.onNativeSurfaceDestroyed();
+    }
+
+    // Called when the surface is resized
+    @Override
+    public void surfaceChanged(SurfaceHolder holder,
+                               int format, int width, int height) {
+        Log.v("SDL", "surfaceChanged()");
+
+        int sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565 by default
+        switch (format) {
+            case PixelFormat.A_8:
+                Log.v("SDL", "pixel format A_8");
+                break;
+            case PixelFormat.LA_88:
+                Log.v("SDL", "pixel format LA_88");
+                break;
+            case PixelFormat.L_8:
+                Log.v("SDL", "pixel format L_8");
+                break;
+            case PixelFormat.RGBA_4444:
+                Log.v("SDL", "pixel format RGBA_4444");
+                sdlFormat = 0x15421002; // SDL_PIXELFORMAT_RGBA4444
+                break;
+            case PixelFormat.RGBA_5551:
+                Log.v("SDL", "pixel format RGBA_5551");
+                sdlFormat = 0x15441002; // SDL_PIXELFORMAT_RGBA5551
+                break;
+            case PixelFormat.RGBA_8888:
+                Log.v("SDL", "pixel format RGBA_8888");
+                sdlFormat = 0x16462004; // SDL_PIXELFORMAT_RGBA8888
+                break;
+            case PixelFormat.RGBX_8888:
+                Log.v("SDL", "pixel format RGBX_8888");
+                sdlFormat = 0x16261804; // SDL_PIXELFORMAT_RGBX8888
+                break;
+            case PixelFormat.RGB_332:
+                Log.v("SDL", "pixel format RGB_332");
+                sdlFormat = 0x14110801; // SDL_PIXELFORMAT_RGB332
+                break;
+            case PixelFormat.RGB_565:
+                Log.v("SDL", "pixel format RGB_565");
+                sdlFormat = 0x15151002; // SDL_PIXELFORMAT_RGB565
+                break;
+            case PixelFormat.RGB_888:
+                Log.v("SDL", "pixel format RGB_888");
+                // Not sure this is right, maybe SDL_PIXELFORMAT_RGB24 instead?
+                sdlFormat = 0x16161804; // SDL_PIXELFORMAT_RGB888
+                break;
+            default:
+                Log.v("SDL", "pixel format unknown " + format);
+                break;
+        }
+
+        mWidth = width;
+        mHeight = height;
+        SDLActivity.onNativeResize(width, height, sdlFormat, mDisplay.getRefreshRate());
+        Log.v("SDL", "Window size: " + width + "x" + height);
+
+
+        boolean skip = false;
+        int requestedOrientation = SDLActivity.mSingleton.getRequestedOrientation();
+
+        if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
+        {
+            // Accept any
+        }
+        else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+        {
+            if (mWidth > mHeight) {
+                skip = true;
+            }
+        } else if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
+            if (mWidth < mHeight) {
+                skip = true;
+            }
+        }
+
+        // Special Patch for Square Resolution: Black Berry Passport
+        if (skip) {
+            double min = Math.min(mWidth, mHeight);
+            double max = Math.max(mWidth, mHeight);
+
+            if (max / min < 1.20) {
+                Log.v("SDL", "Don't skip on such aspect-ratio. Could be a square resolution.");
+                skip = false;
+            }
+        }
+
+        if (skip) {
+            Log.v("SDL", "Skip .. Surface is not ready.");
+            return;
+        }
+
+
+        // Set mIsSurfaceReady to 'true' *before* making a call to handleResume
+        SDLActivity.mIsSurfaceReady = true;
+        SDLActivity.onNativeSurfaceChanged();
+
+
+        if (SDLActivity.mSDLThread == null) {
+            // This is the entry point to the C app.
+            // Start up the C app thread and enable sensor input for the first time
+
+            final Thread sdlThread = new Thread(new SDLMain(), "SDLThread");
+            enableSensor(Sensor.TYPE_ACCELEROMETER, true);
+            sdlThread.start();
+
+            // Set up a listener thread to catch when the native thread ends
+            SDLActivity.mSDLThread = new Thread(new Runnable(){
+                @Override
+                public void run(){
+                    try {
+                        sdlThread.join();
+                    }
+                    catch(Exception e){}
+                    finally{
+                        // Native thread has finished
+                        if (! SDLActivity.mExitCalledFromJava) {
+                            try {
+                                SDLActivity.handleNativeExit();
+                            } catch (Exception e) {
+                                e.printStackTrace();
+                            }
+                        }
+                    }
+                }
+            }, "SDLThreadListener");
+            SDLActivity.mSDLThread.start();
+        }
+
+        if (SDLActivity.mHasFocus) {
+            SDLActivity.handleResume();
+        }
+    }
+
+    // Key events
+    @Override
+    public boolean onKey(View  v, int keyCode, KeyEvent event) {
+        // Dispatch the different events depending on where they come from
+        // Some SOURCE_DPAD or SOURCE_GAMEPAD are also SOURCE_KEYBOARD
+        // So, we try to process them as DPAD or GAMEPAD events first, if that fails we try them as KEYBOARD
+
+        if ( (event.getSource() & InputDevice.SOURCE_GAMEPAD) != 0 ||
+                (event.getSource() & InputDevice.SOURCE_DPAD) != 0 ) {
+            if (event.getAction() == KeyEvent.ACTION_DOWN) {
+                if (SDLActivity.onNativePadDown(event.getDeviceId(), keyCode) == 0) {
+                    return true;
+                }
+            } else if (event.getAction() == KeyEvent.ACTION_UP) {
+                if (SDLActivity.onNativePadUp(event.getDeviceId(), keyCode) == 0) {
+                    return true;
+                }
+            }
+        }
+
+        if( (event.getSource() & InputDevice.SOURCE_KEYBOARD) != 0) {
+            if (event.getAction() == KeyEvent.ACTION_DOWN) {
+                //Log.v("SDL", "key down: " + keyCode);
+                SDLActivity.onNativeKeyDown(keyCode);
+                return true;
+            }
+            else if (event.getAction() == KeyEvent.ACTION_UP) {
+                //Log.v("SDL", "key up: " + keyCode);
+                SDLActivity.onNativeKeyUp(keyCode);
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    // Touch events
+    @Override
+    public boolean onTouch(View v, MotionEvent event) {
+        /* Ref: http://developer.android.com/training/gestures/multi.html */
+        final int touchDevId = event.getDeviceId();
+        final int pointerCount = event.getPointerCount();
+        int action = event.getActionMasked();
+        int pointerFingerId;
+        int mouseButton;
+        int i = -1;
+        float x,y,p;
+
+        // !!! FIXME: dump this SDK check after 2.0.4 ships and require API14.
+        if (event.getSource() == InputDevice.SOURCE_MOUSE && SDLActivity.mSeparateMouseAndTouch) {
+            if (Build.VERSION.SDK_INT < 14) {
+                mouseButton = 1;    // For Android==12 all mouse buttons are the left button
+            } else {
+                try {
+                    mouseButton = (Integer) event.getClass().getMethod("getButtonState").invoke(event);
+                } catch(Exception e) {
+                    mouseButton = 1;    // oh well.
+                }
+            }
+            SDLActivity.onNativeMouse(mouseButton, action, event.getX(0), event.getY(0));
+        } else {
+            switch(action) {
+                case MotionEvent.ACTION_MOVE:
+                    for (i = 0; i < pointerCount; i++) {
+                        pointerFingerId = event.getPointerId(i);
+                        x = event.getX(i) / mWidth;
+                        y = event.getY(i) / mHeight;
+                        p = event.getPressure(i);
+                        if (p > 1.0f) {
+                            // may be larger than 1.0f on some devices
+                            // see the documentation of getPressure(i)
+                            p = 1.0f;
+                        }
+                        SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
+                    }
+                    break;
+
+                case MotionEvent.ACTION_UP:
+                case MotionEvent.ACTION_DOWN:
+                    // Primary pointer up/down, the index is always zero
+                    i = 0;
+                case MotionEvent.ACTION_POINTER_UP:
+                case MotionEvent.ACTION_POINTER_DOWN:
+                    // Non primary pointer up/down
+                    if (i == -1) {
+                        i = event.getActionIndex();
+                    }
+
+                    pointerFingerId = event.getPointerId(i);
+                    x = event.getX(i) / mWidth;
+                    y = event.getY(i) / mHeight;
+                    p = event.getPressure(i);
+                    if (p > 1.0f) {
+                        // may be larger than 1.0f on some devices
+                        // see the documentation of getPressure(i)
+                        p = 1.0f;
+                    }
+                    SDLActivity.onNativeTouch(touchDevId, pointerFingerId, action, x, y, p);
+                    break;
+
+                case MotionEvent.ACTION_CANCEL:
+                    for (i = 0; i < pointerCount; i++) {
+                        pointerFingerId = event.getPointerId(i);
+                        x = event.getX(i) / mWidth;
+                        y = event.getY(i) / mHeight;
+                        p = event.getPressure(i);
+                        if (p > 1.0f) {
+                            // may be larger than 1.0f on some devices
+                            // see the documentation of getPressure(i)
+                            p = 1.0f;
+                        }
+                        SDLActivity.onNativeTouch(touchDevId, pointerFingerId, MotionEvent.ACTION_UP, x, y, p);
+                    }
+                    break;
+
+                default:
+                    break;
+            }
+        }
+
+        return true;
+    }
+
+    // Sensor events
+    public void enableSensor(int sensortype, boolean enabled) {
+        // TODO: This uses getDefaultSensor - what if we have >1 accels?
+        if (enabled) {
+            mSensorManager.registerListener(this,
+                    mSensorManager.getDefaultSensor(sensortype),
+                    SensorManager.SENSOR_DELAY_GAME, null);
+        } else {
+            mSensorManager.unregisterListener(this,
+                    mSensorManager.getDefaultSensor(sensortype));
+        }
+    }
+
+    @Override
+    public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        // TODO
+    }
+
+    @Override
+    public void onSensorChanged(SensorEvent event) {
+        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
+            float x, y;
+            switch (mDisplay.getRotation()) {
+                case Surface.ROTATION_90:
+                    x = -event.values[1];
+                    y = event.values[0];
+                    break;
+                case Surface.ROTATION_270:
+                    x = event.values[1];
+                    y = -event.values[0];
+                    break;
+                case Surface.ROTATION_180:
+                    // Urho3D: fix SDL copy-paste error
+                    x = -event.values[0];
+                    y = -event.values[1];
+                    break;
+                default:
+                    x = event.values[0];
+                    y = event.values[1];
+                    break;
+            }
+            SDLActivity.onNativeAccel(-x / SensorManager.GRAVITY_EARTH,
+                    y / SensorManager.GRAVITY_EARTH,
+                    event.values[2] / SensorManager.GRAVITY_EARTH - 1);
+        }
+    }
+}

+ 5 - 0
Script/AtomicNET/Platform/Android/JavaSDL/src/main/res/values/values.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <eat-comment/>
+    <string name="app_name">AtomicSDL</string>
+</resources>

BIN
Script/AtomicNET/Platform/Android/JavaSDLBin/atomicjavasdl-release.aar


+ 6 - 1
Script/Packages/Atomic/Engine.json

@@ -2,5 +2,10 @@
 	"name" : "Engine",
 	"sources" : ["Source/Atomic/Engine"],
 	"includes" : [],
-	"classes" : ["Engine", "Application"]
+	"classes" : ["Engine", "Application"],
+	"excludes" : {
+		"Application" : {
+			"Run" : []
+		}
+	}
 }

+ 3 - 2
Script/Packages/Atomic/Input.json

@@ -1,7 +1,8 @@
 {
 	"name" : "Input",
 	"sources" : ["Source/Atomic/Input"],
-	"classes" : ["Input"],
+	"includes" : ["<Atomic/UI/UIWidget.h>"],
+	"classes" : ["Input", "JoystickState"],
 	"overloads" : {
 		"Input" : {
 			"BindButton" : ["UIButton", "int"]
@@ -12,7 +13,7 @@
 		     "getTouch(index:number):any;"
         ]
 	},
-    
+
    	"haxe_decl" : {
 		"Input" : [
 		     "getTouch(index:number):Dynamic;"

+ 5 - 1
Script/Packages/AtomicApp/AtomicApp.json

@@ -1,5 +1,9 @@
 {
 	"name" : "AtomicApp",
 	"sources" : ["Source/AtomicApp", "Source/AtomicApp/Player"],
-	"classes" : ["AppBase", "IPCClientApp", "PlayerApp", "IPCPlayerApp"]
+	"classes" : ["AppBase", "PlayerApp", "IPCClientApp", "IPCPlayerApp"],
+	"classExcludes" : {
+		"IPCClientApp" : ["ANDROID", "IOS", "WEB"],
+		"IPCPlayerApp" : ["ANDROID", "IOS", "WEB"]
+	}
 }

+ 1 - 2
Script/Packages/AtomicApp/Package.json

@@ -3,6 +3,5 @@
   "name" : "AtomicApp",
   "namespace" : "Atomic",
   "dependencies" : ["Script/Packages/Atomic"],
-  "modules" : ["AtomicApp"],
-  "platforms" : ["WINDOWS", "MACOSX", "LINUX"]
+  "modules" : ["AtomicApp"]
 }

+ 7 - 2
Script/Packages/AtomicNETNative/AtomicNETNative.json

@@ -1,5 +1,10 @@
 {
 	"name" : "AtomicNETNative",
-	"sources" : ["Source/AtomicNET/NETNative"],
-	"classes" : ["NETCore", "NETIPCPlayerApp", "NETServiceApplication", "NETAtomicPlayer"]
+	"sources" : ["Source/AtomicNET/NETNative", "Source/AtomicNET/NETNative/Desktop"],
+	"classes" : ["NETCore", "NETAtomicPlayer","NETIPCPlayerApp", "NETServiceApplication"],
+	"classExcludes" : {
+		"NETIPCPlayerApp" : ["ANDROID", "IOS", "WEB"],
+		"NETServiceApplication" : ["ANDROID", "IOS", "WEB"]
+	}
+
 }

+ 1 - 1
Script/Packages/AtomicNETNative/Package.json

@@ -4,6 +4,6 @@
   "namespace" : "Atomic",
   "dependencies" : ["Script/Packages/AtomicApp"],
   "modules" : ["AtomicNETNative"],
-  "platforms" : ["WINDOWS", "MACOSX","LINUX"],
+  "platforms" : ["WINDOWS", "MACOSX","LINUX", "ANDROID"],
   "bindings" : ["CSharp"]
 }

+ 1 - 1
Script/Packages/AtomicNETScript/Package.json

@@ -4,5 +4,5 @@
   "namespace" : "Atomic",
   "dependencies" : ["Script/Packages/Atomic"],
   "modules" : ["AtomicNETScript"],
-  "platforms" : ["WINDOWS", "MACOSX","LINUX"]
+  "platforms" : ["WINDOWS", "MACOSX","LINUX", "ANDROID"]
 }

+ 4 - 4
Source/Atomic/Graphics/BillboardSet.h

@@ -47,16 +47,16 @@ public:
     virtual ~Billboard();
 
     const Vector3& GetPosition() const { return position_; }
-    void SetPosition(Vector3 &position) { position_ = position; }
+    void SetPosition(const Vector3 &position) { position_ = position; }
 
     const Vector2 GetSize() const { return size_; }
-    void SetSize(Vector2 &size) { size_ = size; }
+    void SetSize(const Vector2 &size) { size_ = size; }
 
     const Rect& GetUV() const { return uv_; }
-    void SetUV(Rect &uv) { uv_ = uv; }
+    void SetUV(const Rect &uv) { uv_ = uv; }
 
     const Color& GetColor() const { return color_; }
-    void SetColor(Color &color) { color_ = color; }
+    void SetColor(const Color &color) { color_ = color; }
 
     float GetRotation() const { return rotation_; }
     void SetRotation(float rotation) { rotation_ = rotation; }

+ 12 - 1
Source/Atomic/IO/FileSystem.cpp

@@ -1259,12 +1259,23 @@ bool IsAbsoluteParentPath(const String& absParentPath, const String& fullPath)
 String GetSanitizedPath(const String& path)
 {
     String sanitized = GetInternalPath(path);
-
     StringVector parts = sanitized.Split('/');
 
+#ifndef ATOMIC_PLATFORM_WINDOWS
+
+    bool absolute = IsAbsolutePath(path);
     sanitized = String::Joined(parts, "/");
+    if (absolute)
+        sanitized = "/" + sanitized;
+
+#else
+
+    sanitized = String::Joined(parts, "/");
+
+#endif
 
     return sanitized;
+
 }
 
 bool GetRelativePath(const String& fromPath, const String& toPath, String& output)

+ 172 - 112
Source/Atomic/Input/Input.cpp

@@ -1212,10 +1212,11 @@ bool Input::RemoveScreenJoystick(SDL_JoystickID id)
         ATOMIC_LOGERRORF("Failed to remove non-existing screen joystick ID #%d", id);
         return false;
     }
-
+	// ATOMIC BEGIN
+	/*
     JoystickState& state = joysticks_[id];
-// ATOMIC BEGIN
-/*
+
+
     if (!state.screenJoystick_)
     {
         ATOMIC_LOGERRORF("Failed to remove joystick with ID #%d which is not a screen joystick", id);
@@ -1223,20 +1224,21 @@ bool Input::RemoveScreenJoystick(SDL_JoystickID id)
     }
 
      state.screenJoystick_->Remove();
-*/
-// ATOMIC END
-    joysticks_.Erase(id);
 
-    return true;
+    joysticks_.Erase(id);
+*/
+	// ATOMIC END    
+	return true;
 }
 
 void Input::SetScreenJoystickVisible(SDL_JoystickID id, bool enable)
 {
     if (joysticks_.Contains(id))
     {
-        JoystickState& state = joysticks_[id];
-
 // ATOMIC BEGIN
+
+//        JoystickState& state = joysticks_[id];
+
 //        if (state.screenJoystick_)
 //            state.screenJoystick_->SetVisible(enable);
 // ATOMIC END
@@ -1345,8 +1347,14 @@ SDL_JoystickID Input::OpenJoystick(unsigned index)
 
     // Create joystick state for the new joystick
     int joystickID = SDL_JoystickInstanceID(joystick);
-    JoystickState& state = joysticks_[joystickID];
-    state.joystick_ = joystick;
+
+// ATOMIC BEGIN
+	SharedPtr<JoystickState> nstate(new JoystickState());
+    joysticks_[joystickID] = nstate;
+	JoystickState& state = *nstate;
+// ATOMIC END
+    
+	state.joystick_ = joystick;
     state.joystickID_ = joystickID;
     state.name_ = SDL_JoystickName(joystick);
     if (SDL_IsGameController(index))
@@ -1518,30 +1526,36 @@ TouchState* Input::GetTouch(unsigned index) const
 JoystickState* Input::GetJoystickByIndex(unsigned index)
 {
     unsigned compare = 0;
-    for (HashMap<SDL_JoystickID, JoystickState>::Iterator i = joysticks_.Begin(); i != joysticks_.End(); ++i)
+	// ATOMIC BEGIN
+    for (HashMap<SDL_JoystickID, SharedPtr<JoystickState>>::Iterator i = joysticks_.Begin(); i != joysticks_.End(); ++i)
     {
         if (compare++ == index)
-            return &(i->second_);
+            return (i->second_);
     }
+	// ATOMIC END
 
     return 0;
 }
 
 JoystickState* Input::GetJoystickByName(const String& name)
 {
-    for (HashMap<SDL_JoystickID, JoystickState>::Iterator i = joysticks_.Begin(); i != joysticks_.End(); ++i)
+	// ATOMIC BEGIN
+    for (HashMap<SDL_JoystickID, SharedPtr<JoystickState>>::Iterator i = joysticks_.Begin(); i != joysticks_.End(); ++i)
     {
-        if (i->second_.name_ == name)
-            return &(i->second_);
+        if (i->second_->name_ == name)
+            return (i->second_);
     }
+	// ATOMIC END
 
     return 0;
 }
 
 JoystickState* Input::GetJoystick(SDL_JoystickID id)
 {
-    HashMap<SDL_JoystickID, JoystickState>::Iterator i = joysticks_.Find(id);
-    return i != joysticks_.End() ? &(i->second_) : 0;
+	// ATOMIC BEGIN
+    HashMap<SDL_JoystickID, SharedPtr<JoystickState>>::Iterator i = joysticks_.Find(id);
+    return i != joysticks_.End() ? (i->second_) : 0;
+	// ATOMIC END
 }
 
 bool Input::IsScreenJoystickVisible(SDL_JoystickID id) const
@@ -1647,11 +1661,13 @@ void Input::ResetInputAccumulation()
     mouseButtonPress_ = 0;
     mouseMove_ = IntVector2::ZERO;
     mouseMoveWheel_ = 0;
-    for (HashMap<SDL_JoystickID, JoystickState>::Iterator i = joysticks_.Begin(); i != joysticks_.End(); ++i)
+	// ATOMIC BEGIN
+    for (HashMap<SDL_JoystickID, SharedPtr<JoystickState>>::Iterator i = joysticks_.Begin(); i != joysticks_.End(); ++i)
     {
-        for (unsigned j = 0; j < i->second_.buttonPress_.Size(); ++j)
-            i->second_.buttonPress_[j] = false;
+        for (unsigned j = 0; j < i->second_->buttonPress_.Size(); ++j)
+            i->second_->buttonPress_[j] = false;
     }
+	// ATOMIC END
 
     // Reset touch delta movement
     for (HashMap<int, TouchState>::Iterator i = touches_.Begin(); i != touches_.End(); ++i)
@@ -1714,8 +1730,10 @@ void Input::ResetState()
     scancodePress_.Clear();
 
     /// \todo Check if resetting joystick state on input focus loss is even necessary
-    for (HashMap<SDL_JoystickID, JoystickState>::Iterator i = joysticks_.Begin(); i != joysticks_.End(); ++i)
-        i->second_.Reset();
+	// ATOMIC BEGIN
+    for (HashMap<SDL_JoystickID, SharedPtr<JoystickState>>::Iterator i = joysticks_.Begin(); i != joysticks_.End(); ++i)
+        i->second_->Reset();
+	// ATOMIC END
 
     ResetTouches();
 
@@ -2286,22 +2304,28 @@ void Input::HandleSDLEvent(void* sdlEvent)
 
             unsigned button = evt.jbutton.button;
             SDL_JoystickID joystickID = evt.jbutton.which;
-            JoystickState& state = joysticks_[joystickID];
-
-            // Skip ordinary joystick event for a controller
-            if (!state.controller_)
-            {
-                VariantMap& eventData = GetEventDataMap();
-                eventData[P_JOYSTICKID] = joystickID;
-                eventData[P_BUTTON] = button;
 
-                if (button < state.buttons_.Size())
-                {
-                    state.buttons_[button] = true;
-                    state.buttonPress_[button] = true;
-                    SendEvent(E_JOYSTICKBUTTONDOWN, eventData);
-                }
-            }
+			// ATOMIC BEGIN
+			if (joysticks_.Contains(joystickID))
+			{
+				JoystickState* state = joysticks_[joystickID];
+
+				// Skip ordinary joystick event for a controller
+				if (!state->controller_)
+				{
+					VariantMap& eventData = GetEventDataMap();
+					eventData[P_JOYSTICKID] = joystickID;
+					eventData[P_BUTTON] = button;
+
+					if (button < state->buttons_.Size())
+					{
+						state->buttons_[button] = true;
+						state->buttonPress_[button] = true;
+						SendEvent(E_JOYSTICKBUTTONDOWN, eventData);
+					}
+				}
+			}
+			// ATOMIC END
         }
         break;
 
@@ -2311,21 +2335,27 @@ void Input::HandleSDLEvent(void* sdlEvent)
 
             unsigned button = evt.jbutton.button;
             SDL_JoystickID joystickID = evt.jbutton.which;
-            JoystickState& state = joysticks_[joystickID];
-
-            if (!state.controller_)
-            {
-                VariantMap& eventData = GetEventDataMap();
-                eventData[P_JOYSTICKID] = joystickID;
-                eventData[P_BUTTON] = button;
 
-                if (button < state.buttons_.Size())
-                {
-                    if (!state.controller_)
-                        state.buttons_[button] = false;
-                    SendEvent(E_JOYSTICKBUTTONUP, eventData);
-                }
-            }
+			// ATOMIC BEGIN
+			if (joysticks_.Contains(joystickID))
+			{
+				JoystickState* state = joysticks_[joystickID];
+
+				if (!state->controller_)
+				{
+					VariantMap& eventData = GetEventDataMap();
+					eventData[P_JOYSTICKID] = joystickID;
+					eventData[P_BUTTON] = button;
+
+					if (button < state->buttons_.Size())
+					{
+						if (!state->controller_)
+							state->buttons_[button] = false;
+						SendEvent(E_JOYSTICKBUTTONUP, eventData);
+					}
+				}
+			}
+			// ATOMIC END
         }
         break;
 
@@ -2334,24 +2364,31 @@ void Input::HandleSDLEvent(void* sdlEvent)
             using namespace JoystickAxisMove;
 
             SDL_JoystickID joystickID = evt.jaxis.which;
-            JoystickState& state = joysticks_[joystickID];
-
-            if (!state.controller_)
-            {
-                VariantMap& eventData = GetEventDataMap();
-                eventData[P_JOYSTICKID] = joystickID;
-                eventData[P_AXIS] = evt.jaxis.axis;
-                eventData[P_POSITION] = Clamp((float)evt.jaxis.value / 32767.0f, -1.0f, 1.0f);
 
-                if (evt.jaxis.axis < state.axes_.Size())
-                {
-                    // If the joystick is a controller, only use the controller axis mappings
-                    // (we'll also get the controller event)
-                    if (!state.controller_)
-                        state.axes_[evt.jaxis.axis] = eventData[P_POSITION].GetFloat();
-                    SendEvent(E_JOYSTICKAXISMOVE, eventData);
-                }
-            }
+			// ATOMIC BEGIN
+			if (joysticks_.Contains(joystickID))
+			{
+
+				JoystickState* state = joysticks_[joystickID];
+
+				if (!state->controller_)
+				{
+					VariantMap& eventData = GetEventDataMap();
+					eventData[P_JOYSTICKID] = joystickID;
+					eventData[P_AXIS] = evt.jaxis.axis;
+					eventData[P_POSITION] = Clamp((float)evt.jaxis.value / 32767.0f, -1.0f, 1.0f);
+
+					if (evt.jaxis.axis < state->axes_.Size())
+					{
+						// If the joystick is a controller, only use the controller axis mappings
+						// (we'll also get the controller event)
+						if (!state->controller_)
+							state->axes_[evt.jaxis.axis] = eventData[P_POSITION].GetFloat();
+						SendEvent(E_JOYSTICKAXISMOVE, eventData);
+					}
+				}
+			}
+			// ATOMIC END
         }
         break;
 
@@ -2360,18 +2397,23 @@ void Input::HandleSDLEvent(void* sdlEvent)
             using namespace JoystickHatMove;
 
             SDL_JoystickID joystickID = evt.jaxis.which;
-            JoystickState& state = joysticks_[joystickID];
-
-            VariantMap& eventData = GetEventDataMap();
-            eventData[P_JOYSTICKID] = joystickID;
-            eventData[P_HAT] = evt.jhat.hat;
-            eventData[P_POSITION] = evt.jhat.value;
-
-            if (evt.jhat.hat < state.hats_.Size())
-            {
-                state.hats_[evt.jhat.hat] = evt.jhat.value;
-                SendEvent(E_JOYSTICKHATMOVE, eventData);
-            }
+			// ATOMIC BEGIN
+			if (joysticks_.Contains(joystickID))
+			{
+				JoystickState* state = joysticks_[joystickID];
+
+				VariantMap& eventData = GetEventDataMap();
+				eventData[P_JOYSTICKID] = joystickID;
+				eventData[P_HAT] = evt.jhat.hat;
+				eventData[P_POSITION] = evt.jhat.value;
+
+				if (evt.jhat.hat < state->hats_.Size())
+				{
+					state->hats_[evt.jhat.hat] = evt.jhat.value;
+					SendEvent(E_JOYSTICKHATMOVE, eventData);
+				}
+			}
+			// ATOMIC END
         }
         break;
 
@@ -2381,18 +2423,25 @@ void Input::HandleSDLEvent(void* sdlEvent)
 
             unsigned button = evt.cbutton.button;
             SDL_JoystickID joystickID = evt.cbutton.which;
-            JoystickState& state = joysticks_[joystickID];
 
-            VariantMap& eventData = GetEventDataMap();
-            eventData[P_JOYSTICKID] = joystickID;
-            eventData[P_BUTTON] = button;
+			// ATOMIC BEGIN
+			if (joysticks_.Contains(joystickID))
+			{
 
-            if (button < state.buttons_.Size())
-            {
-                state.buttons_[button] = true;
-                state.buttonPress_[button] = true;
-                SendEvent(E_JOYSTICKBUTTONDOWN, eventData);
-            }
+				JoystickState* state = joysticks_[joystickID];
+
+				VariantMap& eventData = GetEventDataMap();
+				eventData[P_JOYSTICKID] = joystickID;
+				eventData[P_BUTTON] = button;
+
+				if (button < state->buttons_.Size())
+				{
+					state->buttons_[button] = true;
+					state->buttonPress_[button] = true;
+					SendEvent(E_JOYSTICKBUTTONDOWN, eventData);
+				}
+			}
+			// ATOMIC END
         }
         break;
 
@@ -2402,17 +2451,23 @@ void Input::HandleSDLEvent(void* sdlEvent)
 
             unsigned button = evt.cbutton.button;
             SDL_JoystickID joystickID = evt.cbutton.which;
-            JoystickState& state = joysticks_[joystickID];
-
-            VariantMap& eventData = GetEventDataMap();
-            eventData[P_JOYSTICKID] = joystickID;
-            eventData[P_BUTTON] = button;
 
-            if (button < state.buttons_.Size())
-            {
-                state.buttons_[button] = false;
-                SendEvent(E_JOYSTICKBUTTONUP, eventData);
-            }
+			// ATOMIC BEGIN
+			if (joysticks_.Contains(joystickID))
+			{
+				JoystickState* state = joysticks_[joystickID];
+
+				VariantMap& eventData = GetEventDataMap();
+				eventData[P_JOYSTICKID] = joystickID;
+				eventData[P_BUTTON] = button;
+
+				if (button < state->buttons_.Size())
+				{
+					state->buttons_[button] = false;
+					SendEvent(E_JOYSTICKBUTTONUP, eventData);
+				}
+			}
+			// ATOMIC END
         }
         break;
 
@@ -2421,18 +2476,23 @@ void Input::HandleSDLEvent(void* sdlEvent)
             using namespace JoystickAxisMove;
 
             SDL_JoystickID joystickID = evt.caxis.which;
-            JoystickState& state = joysticks_[joystickID];
 
-            VariantMap& eventData = GetEventDataMap();
-            eventData[P_JOYSTICKID] = joystickID;
-            eventData[P_AXIS] = evt.caxis.axis;
-            eventData[P_POSITION] = Clamp((float)evt.caxis.value / 32767.0f, -1.0f, 1.0f);
-
-            if (evt.caxis.axis < state.axes_.Size())
-            {
-                state.axes_[evt.caxis.axis] = eventData[P_POSITION].GetFloat();
-                SendEvent(E_JOYSTICKAXISMOVE, eventData);
-            }
+			// ATOMIC BEGIN
+			if (joysticks_.Contains(joystickID))
+			{
+				JoystickState* state = joysticks_[joystickID];
+
+				VariantMap& eventData = GetEventDataMap();
+				eventData[P_JOYSTICKID] = joystickID;
+				eventData[P_AXIS] = evt.caxis.axis;
+				eventData[P_POSITION] = Clamp((float)evt.caxis.value / 32767.0f, -1.0f, 1.0f);
+
+				if (evt.caxis.axis < state->axes_.Size())
+				{
+					state->axes_[evt.caxis.axis] = eventData[P_POSITION].GetFloat();
+					SendEvent(E_JOYSTICKAXISMOVE, eventData);
+				}
+			}
         }
         break;
 

+ 18 - 2
Source/Atomic/Input/Input.h

@@ -76,8 +76,13 @@ struct TouchState
 };
 
 /// %Input state for a joystick.
-struct JoystickState
+// ATOMIC BEGIN
+class JoystickState : public RefCounted
 {
+	ATOMIC_REFCOUNTED(JoystickState)
+// ATOMIC END
+
+public:
     /// Construct with defaults.
     JoystickState() :
         joystick_(0), joystickID_(0), controller_(0),
@@ -329,6 +334,13 @@ public:
     void JoystickSimulateMouseMove(int xpos, int ypos); /// moves the on screen cursor
     void JoystickSimulateMouseButton(int button); /// simulated mouse press down & up
 
+	int GetTouchID(unsigned index) { if (index >= touches_.Size()) return 0; return touches_[index].touchID_; }
+	const IntVector2& GetTouchPosition(unsigned index) { if (index >= touches_.Size()) return IntVector2::ZERO; return touches_[index].position_; }
+	const IntVector2& GetTouchLastPosition(unsigned index) { if (index >= touches_.Size()) return IntVector2::ZERO; return touches_[index].lastPosition_; }
+	const IntVector2& GetTouchDelta(unsigned index) { if (index >= touches_.Size()) return IntVector2::ZERO; return touches_[index].delta_; }
+	const float GetTouchPressure(unsigned index) { if (index >= touches_.Size()) return 0.0f; return touches_[index].pressure_; }
+	UIWidget* GetTouchWidget(unsigned index) { if (index >= touches_.Size()) return 0; return touches_[index].touchedWidget_; }	
+
 // ATOMIC END
 
 private:
@@ -413,8 +425,12 @@ private:
     HashMap<int, int> touchIDMap_;
     /// String for text input.
     String textInput_;
+
+// ATOMIC BEGIN
     /// Opened joysticks.
-    HashMap<SDL_JoystickID, JoystickState> joysticks_;
+    HashMap<SDL_JoystickID, SharedPtr<JoystickState>> joysticks_;
+// ATOMIC END
+
     /// Mouse buttons' down state.
     unsigned mouseButtonDown_;
     /// Mouse buttons' pressed state.

+ 13 - 0
Source/AtomicApp/AppBase.cpp

@@ -114,6 +114,19 @@ namespace Atomic
 
     }
 
+	bool AppBase::RunFrame()
+	{
+		engine_->RunFrame();
+
+		if (engine_->IsExiting())
+		{
+			return false;
+		}
+
+		return true;
+
+	}
+
     void AppBase::Stop()
     {
 

+ 5 - 0
Source/AtomicApp/AppBase.h

@@ -45,6 +45,11 @@ namespace Atomic
         /// Cleanup after the main loop. 
         virtual void Stop();
 
+		/// Run a single frame, return's true if engine is exiting
+		bool RunFrame();
+
+		virtual void Shutdown() { Stop(); }
+
         static void AddArgument(const String& argument) { arguments_.Push(argument); }
 
         virtual void ProcessArguments();

+ 1 - 1
Source/AtomicEditor/CMakeLists.txt

@@ -10,7 +10,7 @@ file (GLOB_RECURSE SOURCE_FILES *.cpp *.h)
 # Remove the web helper sources
 list(REMOVE_ITEM SOURCE_FILES ${CMAKE_CURRENT_SOURCE_DIR}/WebView/WebProcessHelperMac.cpp)
 
-file (GLOB JAVASCRIPT_BINDINGS_SOURCE ${CMAKE_SOURCE_DIR}/Artifacts/Build/Source/Generated/${JAVASCRIPT_BINDINGS_PLATFORM}/Javascript/Packages/Editor/*.cpp)
+file (GLOB JAVASCRIPT_BINDINGS_SOURCE ${CMAKE_SOURCE_DIR}/Artifacts/Build/Source/Generated/Javascript/Packages/Editor/*.cpp)
 
 set (SOURCE_FILES ${SOURCE_FILES} ${JAVASCRIPT_BINDINGS_SOURCE})
 

+ 27 - 2
Source/AtomicEditor/EditorMode/AEEditorMode.cpp

@@ -33,6 +33,7 @@
 #include <ToolCore/ToolSystem.h>
 #include <ToolCore/License/LicenseSystem.h>
 #include <ToolCore/Project/Project.h>
+#include <ToolCore/Project/ProjectSettings.h>
 
 #include <AtomicJS/Javascript/JSIPCEvents.h>
 
@@ -140,12 +141,36 @@ bool EditorMode::PlayProject(String addArgs, bool debug)
 
     String playerBinary = env->GetEditorBinary();
 
+	ProjectSettings* settings = project->GetProjectSettings();
+
+	String projectAssembly = settings->GetName() + ".dll";
+	String projectExe = settings->GetName() + ".exe";
+
     // TODO: We need to configure project as managed
     bool managed = false;
-    if (fileSystem->FileExists(project->GetResourcePath() + "AtomicProject.dll"))
+    if (fileSystem->FileExists(project->GetResourcePath() + projectAssembly))
     {
         managed = true;
-        playerBinary = env->GetAtomicNETManagedIPCPlayerBinary();
+
+#ifdef ATOMIC_DEV_BUILD
+
+#ifdef ATOMIC_DEBUG		
+		playerBinary = project->GetProjectPath() + "AtomicNET/Debug/Bin/Desktop/" + projectExe;
+#else
+		playerBinary = project->GetProjectPath() + "AtomicNET/Release/Bin/Desktop/" + projectExe;
+#endif
+
+#else
+		// TODO: We are using the release build of the managed project here, how and when to use debug?
+		playerBinary = project->GetProjectPath() + "AtomicNET/Release/Bin/Desktop/" + projectExe;
+#endif
+
+        
+		if (!fileSystem->FileExists(playerBinary))
+		{
+			ATOMIC_LOGERRORF("Managed player: %s does not exist", playerBinary.CString());
+		}
+
     }
 
 

+ 1 - 1
Source/AtomicJS/CMakeLists.txt

@@ -15,7 +15,7 @@ if (NOT MSVC)
     add_definitions (-DUNIX)
 endif()
 
-file (GLOB JAVASCRIPT_BINDINGS_SOURCE ${CMAKE_SOURCE_DIR}/Artifacts/Build/Source/Generated/${JAVASCRIPT_BINDINGS_PLATFORM}/Javascript/Packages/Atomic/*.cpp)
+file (GLOB JAVASCRIPT_BINDINGS_SOURCE ${CMAKE_SOURCE_DIR}/Artifacts/Build/Source/Generated/Javascript/Packages/Atomic/*.cpp)
 
 set (SOURCE_FILES ${JAVASCRIPT_SOURCE} ${JAVASCRIPT_BINDINGS_SOURCE})
 

+ 38 - 5
Source/AtomicNET/NETNative/CMakeLists.txt

@@ -5,7 +5,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}
                     ${CMAKE_SOURCE_DIR}/Source/ThirdParty/FreeType/include
                     ${CMAKE_SOURCE_DIR}/Source/ThirdParty/Box2D)
 
-set (CSATOMICDIR "${CMAKE_SOURCE_DIR}/Artifacts/Build/Source/Generated/${JAVASCRIPT_BINDINGS_PLATFORM}/CSharp/Packages/")
+set (CSATOMICDIR "${CMAKE_SOURCE_DIR}/Artifacts/Build/Source/Generated/CSharp/Packages/")
 
 file (GLOB CSHARP_BINDINGS_SOURCE ${CSATOMICDIR}/Atomic/Native/*.cpp ${CSATOMICDIR}/Atomic/Native/*.h
                                   ${CSATOMICDIR}/AtomicNETNative/Native/*.cpp ${CSATOMICDIR}/AtomicNETNative/Native/*.h
@@ -13,9 +13,19 @@ file (GLOB CSHARP_BINDINGS_SOURCE ${CSATOMICDIR}/Atomic/Native/*.cpp ${CSATOMICD
                                   ${CSATOMICDIR}/AtomicApp/Native/*.cpp ${CSATOMICDIR}/AtomicApp/Native/*.h
                                   ${CSATOMICDIR}/AtomicPlayer/Native/*.cpp ${CSATOMICDIR}/AtomicPlayer/Native/*.h )
 
-file (GLOB_RECURSE SOURCE_FILES *.cpp *.h)
+file (GLOB SOURCE_FILES *.cpp *.h)
 
-add_library(AtomicNETNative SHARED ${SOURCE_FILES}  ${CSHARP_BINDINGS_SOURCE})
+if (NOT IOS AND NOT ANDROID AND NOT EMSCRIPTEN)
+    file (GLOB DESKTOP_SOURCE_FILES Desktop/*.cpp Desktop/*.h)
+    set (SOURCE_FILES ${SOURCE_FILES} ${DESKTOP_SOURCE_FILES})
+endif()
+
+if (ANDROID)
+	include_directories(${CMAKE_SOURCE_DIR}/Source/ThirdParty/SDL/include)
+	set (SOURCE_FILES ${SOURCE_FILES} ${CMAKE_SOURCE_DIR}/Source/ThirdParty/SDL/src/main/android/SDL_android_main.c)
+endif()
+
+add_library(AtomicNETNative SHARED ${SOURCE_FILES} ${CSHARP_BINDINGS_SOURCE})
 
 add_dependencies(AtomicNETNative AtomicToolCheckScripts)
 
@@ -38,6 +48,29 @@ if (APPLE)
 
 endif()
 
+if (MSVC)
+    set (ATOMICNET_NATIVE_PLATFORM Windows)
+elseif(APPLE)
+    if (IOS)
+        set (ATOMICNET_NATIVE_PLATFORM iOS)
+    else()
+        set (ATOMICNET_NATIVE_PLATFORM Mac)
+    endif()
+elseif(LINUX)
+  set (ATOMICNET_NATIVE_PLATFORM Linux)
+elseif(ANDROID)
+    set (ATOMICNET_NATIVE_PLATFORM Android)
+endif()
+
+set (ATOMICNET_NATIVE_DIR "${CMAKE_SOURCE_DIR}/Artifacts/AtomicNET/$<$<CONFIG:debug>:Debug>$<$<CONFIG:release>:Release>/Native/${ATOMICNET_NATIVE_PLATFORM}")
+
 add_custom_command( TARGET AtomicNETNative POST_BUILD
-                    COMMAND "${CMAKE_COMMAND}" ARGS -E make_directory \"${CMAKE_SOURCE_DIR}/Artifacts/AtomicNET/$<$<CONFIG:debug>:Debug>$<$<CONFIG:release>:Release>\"
-                    COMMAND "${CMAKE_COMMAND}" ARGS -E copy_if_different \"$<TARGET_FILE:AtomicNETNative>\" \"${CMAKE_SOURCE_DIR}/Artifacts/AtomicNET/$<$<CONFIG:debug>:Debug>$<$<CONFIG:release>:Release>\" )
+                    COMMAND "${CMAKE_COMMAND}" ARGS -E make_directory "\"${ATOMICNET_NATIVE_DIR}\""
+                    COMMAND "${CMAKE_COMMAND}" ARGS -E copy_if_different \"$<TARGET_FILE:AtomicNETNative>\" "\"${ATOMICNET_NATIVE_DIR}\"" )
+
+if (MSVC)
+
+    # Copy the D3D shader compiler (for pre-Windows 8)
+    add_custom_command( TARGET AtomicNETNative POST_BUILD
+                        COMMAND ${CMAKE_COMMAND} ARGS -E copy_if_different \"${D3DCOMPILER_47_DLL}\" \"${ATOMICNET_NATIVE_DIR}/D3DCompiler_47.dll\" )
+endif(MSVC)

+ 9 - 23
Source/AtomicNET/NETNative/NETIPCPlayerApp.cpp → Source/AtomicNET/NETNative/Desktop/NETIPCPlayerApp.cpp

@@ -49,6 +49,15 @@ namespace Atomic
     void NETIPCPlayerApp::Setup()
     {
         IPCPlayerApp::Setup();
+
+		// TODO: we should always have a --project for IPCPlayer, however it is doing 
+		// double duty right now as managed player
+		StringVector args = GetArguments();
+		if (!args.Contains("--project"))
+		{
+			engineParameters_["ResourcePrefixPaths"] = "AtomicPlayer_Resources";
+			engineParameters_["ResourcePaths"] = "AtomicResources";
+		}
     }
 
     int NETIPCPlayerApp::Initialize()
@@ -73,30 +82,7 @@ namespace Atomic
 
         return 0;
     }
-
-    bool NETIPCPlayerApp::RunFrame()
-    {
-        engine_->RunFrame();
-
-        if (engine_->IsExiting())
-        {            
-            return false;
-        }
-
-        return true;
-        
-    }
     
-    void NETIPCPlayerApp::Shutdown()
-    {
-        Stop();
-    }
-
-    void NETIPCPlayerApp::Stop()
-    {
-        IPCPlayerApp::Stop();
-    }
-
     NETIPCPlayerApp* NETIPCPlayerApp::CreateInternal()
     {
         return new NETIPCPlayerApp(NETCore::GetContext());

+ 0 - 6
Source/AtomicNET/NETNative/NETIPCPlayerApp.h → Source/AtomicNET/NETNative/Desktop/NETIPCPlayerApp.h

@@ -38,10 +38,6 @@ namespace Atomic
 
         int Initialize();
 
-        bool RunFrame();
-
-        void Shutdown();
-
     private:
 
         /// Construct.
@@ -49,8 +45,6 @@ namespace Atomic
 
         /// Setup before engine initialization.
         virtual void Setup();
-        /// Cleanup after the main loop.
-        virtual void Stop();
 
     };
 

+ 0 - 17
Source/AtomicNET/NETNative/NETServiceApplication.cpp → Source/AtomicNET/NETNative/Desktop/NETServiceApplication.cpp

@@ -77,23 +77,6 @@ namespace Atomic
         return 0;
     }
 
-    bool NETServiceApplication::RunFrame()
-    {
-        engine_->RunFrame();
-
-        if (engine_->IsExiting())
-        {
-            return false;
-        }
-
-        return true;
-    }
-
-    void NETServiceApplication::Shutdown()
-    {
-        Stop();
-    }
-
     void NETServiceApplication::Stop()
     {
         Application::Stop();

+ 0 - 4
Source/AtomicNET/NETNative/NETServiceApplication.h → Source/AtomicNET/NETNative/Desktop/NETServiceApplication.h

@@ -38,10 +38,6 @@ namespace Atomic
 
         int Initialize();
 
-        bool RunFrame();
-
-        void Shutdown();
-
     private:
 
         /// Construct.

+ 26 - 18
Source/AtomicNET/NETNative/NETAtomicPlayer.cpp

@@ -38,6 +38,30 @@
 #include <stdio.h>
 #endif
 
+// Android or iOS: use SDL_main
+#if defined(__ANDROID__) || defined(IOS)
+
+typedef int(*sdl_entry_callback)();
+
+static sdl_entry_callback sdlEntryCallback = 0;
+
+extern "C" void RegisterSDLEntryCallback(sdl_entry_callback callback)
+{
+	sdlEntryCallback = callback;
+}
+
+extern "C" int SDL_main(int argc, char** argv); 
+int SDL_main(int argc, char** argv) 
+{ 
+    Atomic::ParseArguments(argc, argv); 
+	if (sdlEntryCallback != 0)
+	{
+		return sdlEntryCallback();
+	}
+	return 0;
+}
+#endif
+
 namespace Atomic
 {
 
@@ -50,6 +74,8 @@ namespace Atomic
     void NETAtomicPlayer::Setup()
     {
         PlayerApp::Setup();
+
+		engineParameters_["ResourcePaths"] = "AtomicResources"; 
     }
 
     int NETAtomicPlayer::Initialize()
@@ -78,24 +104,6 @@ namespace Atomic
         return 0;
     }
 
-    bool NETAtomicPlayer::RunFrame()
-    {
-        engine_->RunFrame();
-
-        if (engine_->IsExiting())
-        {
-            return false;
-        }
-
-        return true;
-
-    }
-
-    void NETAtomicPlayer::Shutdown()
-    {
-        Stop();
-    }
-
     void NETAtomicPlayer::Stop()
     {
         PlayerApp::Stop();

+ 0 - 4
Source/AtomicNET/NETNative/NETAtomicPlayer.h

@@ -38,10 +38,6 @@ namespace Atomic
 
         int Initialize();
 
-        bool RunFrame();
-
-        void Shutdown();
-
     private:
 
         /// Construct.

+ 3 - 0
Source/AtomicNET/NETNative/NETCInterop.cpp

@@ -113,10 +113,13 @@ namespace Atomic
         // IPC
         ATOMIC_EXPORT_API void csi_Atomic_IPC_SendEventToBrokerWithEventData(IPC* ipc, const char* eventType, ScriptVariantMap* variantMap)
         {
+
+#ifdef ATOMIC_PLATFORM_DESKTOP
             if (variantMap)
                 ipc->SendEventToBroker(eventType, variantMap->GetVariantMap());
             else
                 ipc->SendEventToBroker(eventType);
+#endif
 
         }
 

+ 2 - 2
Source/AtomicNET/NETScript/CMakeLists.txt

@@ -1,8 +1,8 @@
 include_directories(${CMAKE_CURRENT_SOURCE_DIR}
                     ${CMAKE_SOURCE_DIR}/Source/ThirdParty)
 
-set (CSATOMICNETSCRIPTNATIVEDIR "${CMAKE_SOURCE_DIR}/Artifacts/Build/Source/Generated/${JAVASCRIPT_BINDINGS_PLATFORM}/CSharp/Packages/AtomicNETScript/Native")
-set (JSATOMICNETSCRIPTDIR "${CMAKE_SOURCE_DIR}/Artifacts/Build/Source/Generated/${JAVASCRIPT_BINDINGS_PLATFORM}/Javascript/Packages/AtomicNETScript")
+set (CSATOMICNETSCRIPTNATIVEDIR "${CMAKE_SOURCE_DIR}/Artifacts/Build/Source/Generated/CSharp/Packages/AtomicNETScript/Native")
+set (JSATOMICNETSCRIPTDIR "${CMAKE_SOURCE_DIR}/Artifacts/Build/Source/Generated/Javascript/Packages/AtomicNETScript")
 
 file ( GLOB ATOMICNETSCRIPT_BINDINGS_SOURCE ${CSATOMICNETSCRIPTNATIVEDIR}/*.cpp ${CSATOMICNETSCRIPTNATIVEDIR}/*.h
                                             ${JSATOMICNETSCRIPTDIR}/*.cpp ${JSATOMICNETSCRIPTDIR}/*.h )

+ 1 - 1
Source/AtomicNET/NETScript/CSComponent.cpp

@@ -111,7 +111,7 @@ void CSComponent::SendLoadEvent()
 
     VariantMap eventData;
 
-    eventData[P_ASSEMBLYPATH] = assemblyFile_->GetFullPath();
+    eventData[P_ASSEMBLYPATH] = GetFileName(assemblyFile_->GetFullPath());
     eventData[P_CLASSNAME] = componentClassName_;
     eventData[P_NATIVEINSTANCE] = (void*) this;
 

+ 1 - 3
Source/AtomicNET/NETScript/CSComponentAssembly.cpp

@@ -231,9 +231,7 @@ namespace Atomic
 
     bool CSComponentAssembly::BeginLoad(Deserializer& source)
     {
-        File* file = (File*) &source;
-
-        fullAssemblyPath_ = file->GetFullPath();
+		fullAssemblyPath_ = source.GetName();
 
         VariantMap eventData;
 

+ 1 - 1
Source/AtomicPlayerJS/CMakeLists.txt

@@ -2,7 +2,7 @@ include_directories (${CMAKE_SOURCE_DIR}/Source/ThirdParty)
 
 file (GLOB_RECURSE SOURCE_FILES *.cpp *.h)
 
-file (GLOB JAVASCRIPT_BINDINGS_SOURCE ${CMAKE_SOURCE_DIR}/Artifacts/Build/Source/Generated/${JAVASCRIPT_BINDINGS_PLATFORM}/Javascript/Packages/AtomicPlayer/*.cpp)
+file (GLOB JAVASCRIPT_BINDINGS_SOURCE ${CMAKE_SOURCE_DIR}/Artifacts/Build/Source/Generated/Javascript/Packages/AtomicPlayer/*.cpp)
 
 set (SOURCE_FILES ${SOURCE_FILES} ${JAVASCRIPT_BINDINGS_SOURCE} )
 

+ 5 - 1
Source/AtomicTool/AtomicTool.cpp

@@ -269,7 +269,11 @@ void AtomicTool::Start()
     {
         FileSystem* fileSystem = GetSubsystem<FileSystem>();
 
-        String projectDirectory = fileSystem->GetCurrentDir();
+		String projectDirectory = cmd->GetProjectPath();
+			
+		// default to current directly if command doesn't provide the path
+		if (!projectDirectory.Length())
+			projectDirectory = fileSystem->GetCurrentDir();
 
         Vector<String> projectFiles;
         fileSystem->ScanDir(projectFiles, projectDirectory, "*.atomic", SCAN_FILES, false);

+ 2 - 2
Source/AtomicTool/CMakeLists.txt

@@ -19,7 +19,7 @@ add_custom_target( AtomicToolCheckScripts
                    WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
                    COMMAND "${CMAKE_COMMAND}" -E make_directory \"${CMAKE_SOURCE_DIR}/Artifacts/Build/AtomicTool\"
                    COMMAND "${CMAKE_COMMAND}" -E copy_if_different \"$<TARGET_FILE:AtomicTool>\" \"${CMAKE_SOURCE_DIR}/Artifacts/Build/AtomicTool/\"
-                   COMMAND ${ATOMIC_NODE_JAKE};build:genscripts[${JAVASCRIPT_BINDINGS_PLATFORM},false] )
+                   COMMAND ${ATOMIC_NODE_JAKE};build:genscripts[false] )
 
 add_dependencies (AtomicToolCheckScripts AtomicTool)
 
@@ -46,7 +46,7 @@ endif(MSVC)
 
 add_custom_target ( GenerateScriptBindings
                     WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}"
-                    COMMAND ${ATOMIC_NODE_JAKE};build:genscripts[${JAVASCRIPT_BINDINGS_PLATFORM},true];--trace
+                    COMMAND ${ATOMIC_NODE_JAKE};build:genscripts[true];--trace
                     DEPENDS AtomicTool )
 
 add_custom_target ( GenerateAtomicNET

+ 1 - 1
Source/AtomicWebView/CMakeLists.txt

@@ -3,7 +3,7 @@ include_directories (${CMAKE_SOURCE_DIR}/Source/ThirdParty/)
 
 file (GLOB_RECURSE SOURCE_FILES *.cpp *.h)
 
-file (GLOB JAVASCRIPT_BINDINGS_SOURCE ${CMAKE_SOURCE_DIR}/Artifacts/Build/Source/Generated/${JAVASCRIPT_BINDINGS_PLATFORM}/Javascript/Packages/WebView/*.cpp)
+file (GLOB JAVASCRIPT_BINDINGS_SOURCE ${CMAKE_SOURCE_DIR}/Artifacts/Build/Source/Generated/Javascript/Packages/WebView/*.cpp)
 
 if (APPLE)
     set (PLATFORM_SOURCE WebBrowserHost.mm)

+ 6 - 2
Source/CMakeLists.txt

@@ -12,14 +12,18 @@ endif()
 add_subdirectory(AtomicPlayer)
 add_subdirectory(AtomicPlayerJS)
 
-if (NOT IOS AND NOT ANDROID AND NOT EMSCRIPTEN)
+if (NOT IOS AND NOT EMSCRIPTEN)
     add_subdirectory(AtomicApp)
+    add_subdirectory(AtomicNET)
+endif()
+
+if (NOT IOS AND NOT ANDROID AND NOT EMSCRIPTEN)
     add_subdirectory(ToolCore)
     add_subdirectory(ToolCoreJS)
     add_subdirectory(AtomicEditor)
     add_subdirectory(AtomicTool)
     add_subdirectory(Tools)
-    add_subdirectory(AtomicNET)
 endif()
 
+
 include(AtomicDocList)

+ 10 - 0
Source/ThirdParty/SDL/src/core/android/SDL_android.c

@@ -801,6 +801,11 @@ fallback:
                 "open", "(Ljava/lang/String;I)Ljava/io/InputStream;");
         inputStream = (*mEnv)->CallObjectMethod(mEnv, assetManager, mid, fileNameJString, 1 /* ACCESS_RANDOM */);
         if (Android_JNI_ExceptionOccurred(SDL_FALSE)) {
+
+// ATOMIC BEGIN
+// openAPKExpansionInputStream not supported under AtomicNET for now
+#ifdef ATOMIC_DISABLED
+
             /* Try fallback to APK expansion files */
             mid = (*mEnv)->GetMethodID(mEnv, (*mEnv)->GetObjectClass(mEnv, context),
                 "openAPKExpansionInputStream", "(Ljava/lang/String;)Ljava/io/InputStream;");
@@ -816,6 +821,11 @@ fallback:
             if (Android_JNI_ExceptionOccurred(SDL_FALSE) || !inputStream) {
                 goto failure;
             }
+#endif
+            goto failure;
+
+// ATOMIC END
+
         }
 
         ctx->hidden.androidio.inputStreamRef = (*mEnv)->NewGlobalRef(mEnv, inputStream);

+ 0 - 1
Source/ToolCore/Assets/Asset.cpp

@@ -183,7 +183,6 @@ bool Asset::Load()
     dirty_ = false;
     if (!CheckCacheFile())
     {
-        ATOMIC_LOGINFOF("CheckCacheFile:false - %s", path_.CString());
         dirty_ = true;
     }
 

+ 14 - 2
Source/ToolCore/Build/BuildBase.cpp

@@ -46,7 +46,9 @@ BuildBase::BuildBase(Context * context, Project* project, PlatformID platform) :
     containsMDL_(false),
     buildFailed_(false),
     assetBuildTag_(String::EMPTY),
-    fileIncludedResourcesLog_(nullptr)
+    fileIncludedResourcesLog_(nullptr),
+	resourcesOnly_(false),
+	verbose_(false)
 {
     if (UseResourcePackager())
         resourcePackager_ = new ResourcePackager(context, this);
@@ -332,8 +334,12 @@ void BuildBase::BuildDefaultResourceEntries()
         for (unsigned i = 0; i < fileNames.Size(); i++)
         {
             const String& filename = fileNames[i];
-
             AddToResourcePackager(filename, resourceDir);
+
+			if (verbose_)
+			{
+				ATOMIC_LOGINFO(ToString("Default Resource Added: %s%s", resourceDir.CString(), filename.CString()));
+			}
         }
     }
 }
@@ -376,6 +382,12 @@ void BuildBase::BuildAllProjectResourceEntries()
         for (unsigned i = 0; i < fileNamesInProject.Size(); i++)
         {
             AddToResourcePackager(fileNamesInProject[i], projectResourceDir);
+
+			if (verbose_)
+			{
+				ATOMIC_LOGINFO(ToString("Project Resource Added: %s%s", projectResourceDir.CString(), fileNamesInProject[i].CString()));
+			}
+
         }
     }
 }

+ 7 - 0
Source/ToolCore/Build/BuildBase.h

@@ -76,6 +76,11 @@ public:
     /// in the build. If no tag is specified, then all resources are included.
     void SetAssetBuildTag(const String assetBuildTag) { assetBuildTag_ = assetBuildTag; }
 
+	void SetVerbose(bool verbose = true) { verbose_ = verbose; }
+
+	bool GetResourcesOnly() const { return resourcesOnly_; }
+	void SetResourcesOnly(bool resourcesOnly = true) { resourcesOnly_ = resourcesOnly;  }
+
 protected:
 
     bool BuildClean(const String& path);
@@ -108,6 +113,8 @@ protected:
     /// Pointer to a file used to capture the resources included in the build
     File *fileIncludedResourcesLog_;
 
+	bool resourcesOnly_;
+	bool verbose_;
 
 private:
     void BuildFilteredProjectResourceEntries();

+ 25 - 24
Source/ToolCore/Build/BuildWindows.cpp

@@ -28,6 +28,7 @@
 #include "../ToolSystem.h"
 #include "../ToolEnvironment.h"
 #include "../Project/Project.h"
+#include "../Project/ProjectSettings.h"
 #include "../Assets/AssetDatabase.h"
 
 #include "BuildWindows.h"
@@ -91,27 +92,30 @@ bool BuildWindows::CheckIncludeResourceFile(const String& resourceDir, const Str
     return BuildBase::CheckIncludeResourceFile(resourceDir, fileName);
 }
 
-void BuildWindows::BuildManaged(const String& buildPath)
+bool BuildWindows::BuildManaged(const String& buildPath)
 {
-    BuildLog("Building Managed Application");
-
     ToolEnvironment* tenv = GetSubsystem<ToolEnvironment>();
     ToolSystem* toolSystem = GetSubsystem<ToolSystem>();
     FileSystem* fileSystem = GetSubsystem<FileSystem>();
     Project* project = toolSystem->GetProject();
+	ProjectSettings* settings = project->GetProjectSettings();
+
+	String projectPath = project->GetProjectPath();
+	String releaseBins = projectPath + "AtomicNET/Release/Bin/Desktop/";
+	String releaseExe = releaseBins + settings->GetName() + ".exe";
+
+	if (!fileSystem->FileExists(releaseExe))
+	{
+		BuildError(ToString("Error building managed project, please compile the release binary %s before building", releaseExe.CString()));
+		return false;
+	}
     
     StringVector results;
     StringVector filtered;
-    fileSystem->ScanDir(results, tenv->GetAtomicNETCoreAssemblyDir(), "", SCAN_FILES, false);
 
-    StringVector filterList;
+    fileSystem->ScanDir(results, releaseBins, "", SCAN_FILES, false);
 
-    filterList.Push("AtomicIPCPlayer");
-    filterList.Push(".pdb");
-    filterList.Push("System.Collections.Immutable");
-    filterList.Push("System.Reflection.Metadata");
-    filterList.Push("ComponentTest");
-    filterList.Push("AtomicNETService");
+    StringVector filterList;
 
     StringVector::Iterator itr = results.Begin();
     while (itr != results.End())
@@ -129,23 +133,16 @@ void BuildWindows::BuildManaged(const String& buildPath)
         itr++;
     }
 
-    String playerBinary = tenv->GetAtomicNETManagedPlayerBinary();
-   
-    if (!BuildCopyFile(playerBinary, buildPath_ + "/AtomicPlayer.exe"))
-        return;
-
     for (unsigned i = 0; i < filtered.Size(); i++)
     {
         String filename = filtered[i];
 
-        if (!BuildCopyFile(tenv->GetAtomicNETCoreAssemblyDir() + filename, buildPath_ + "/" + filename))
-            return;
+        if (!BuildCopyFile(releaseBins + filename, buildPath_ + "/" + filename))
+            return false;
 
     }
-    if (!BuildCopyFile(project->GetResourcePath() + "/AtomicProject.dll", buildPath_ + "/AtomicProject.dll"))
-        return;
 
-   
+	return true;
   
 }
 
@@ -198,6 +195,9 @@ void BuildWindows::Build(const String& buildPath)
     if (buildFailed_)
         return;
 
+	if (resourcesOnly_)
+		return;
+
     if (!BuildCreateDirectory(buildPath_ + "/Settings"))
         return;
 
@@ -209,11 +209,12 @@ void BuildWindows::Build(const String& buildPath)
             return;
     }
 
-    // TODO: Set project as managed and don't key off AtomicProject.dll
+    // TODO: Set project as managed and don't key off project assembly
 
-    if (fileSystem->FileExists(project->GetResourcePath() + "/AtomicProject.dll"))
+    if (fileSystem->FileExists(project->GetResourcePath() + project->GetProjectSettings()->GetName() + ".dll"))
     {
-        BuildManaged(buildPath);
+		if (!BuildManaged(buildPath))
+			return;
     }
     else
     {

+ 1 - 1
Source/ToolCore/Build/BuildWindows.h

@@ -50,7 +50,7 @@ protected:
 private:
 
     void BuildNative(const String& buildPath);
-    void BuildManaged(const String& buildPath);
+    bool BuildManaged(const String& buildPath);
 
 };
 

+ 1 - 0
Source/ToolCore/Build/ResourcePackager.cpp

@@ -24,6 +24,7 @@
 //
 
 #include "Atomic/Core/StringUtils.h"
+#include <Atomic/IO/Log.h>
 #include <Atomic/IO/FileSystem.h>
 #include <Atomic/Container/ArrayPtr.h>
 

+ 1 - 9
Source/ToolCore/Command/BindCmd.cpp

@@ -50,7 +50,6 @@ bool BindCmd::Parse(const Vector<String>& arguments, unsigned startIndex, String
     String argument = arguments[startIndex].ToLower();
     sourceRootFolder_ = startIndex + 1 < arguments.Size() ? arguments[startIndex + 1] : String::EMPTY;
     packageFolder_ = startIndex + 2 < arguments.Size() ? arguments[startIndex + 2] : String::EMPTY;
-    bindPlatform_ = startIndex + 3 < arguments.Size() ? arguments[startIndex + 3] : String::EMPTY;
 
     if (argument != "bind")
     {
@@ -71,13 +70,6 @@ bool BindCmd::Parse(const Vector<String>& arguments, unsigned startIndex, String
         return false;
     }
 
-    if (!bindPlatform_.Length())
-    {
-        errorMsg = "Unable to parse bind command";
-        return false;
-    }
-
-
     return true;
 }
 
@@ -88,7 +80,7 @@ void BindCmd::Run()
     context_->RegisterSubsystem(jsbind);
 
     ATOMIC_LOGINFOF("Loading Package");
-    jsbind->LoadPackage(sourceRootFolder_, packageFolder_, bindPlatform_);
+    jsbind->LoadPackage(sourceRootFolder_, packageFolder_);
 
     jsbind->GenerateJavaScriptBindings();
 

+ 0 - 1
Source/ToolCore/Command/BindCmd.h

@@ -46,7 +46,6 @@ public:
 
 private:
 
-    String bindPlatform_;
     String sourceRootFolder_;
     String packageFolder_;
 

+ 0 - 6
Source/ToolCore/Command/BuildCmd.cpp

@@ -100,12 +100,6 @@ void BuildCmd::Run()
         return;
     }
 
-    if (!project->ContainsPlatform(platform->GetPlatformID()))
-    {
-        Error(ToString("Project does not contain platform: %s", buildPlatform_.CString()));
-        return;
-    }
-
     // create the build
     BuildBase* buildBase = platform->NewBuild(project);
     if (!assetsBuildTag_.Empty())

+ 2 - 0
Source/ToolCore/Command/Command.h

@@ -63,6 +63,8 @@ public:
 
     virtual bool RequiresProjectLoad() { return true; }
 
+	virtual const String& GetProjectPath() const { return String::EMPTY; }
+
     virtual bool RequiresLicenseValidation() { return false; }
 
 private:

+ 0 - 5
Source/ToolCore/Command/CommandParser.cpp

@@ -23,7 +23,6 @@
 #include "CommandParser.h"
 
 #include "NewProjectCmd.h"
-#include "PlatformAddCmd.h"
 #include "BuildCmd.h"
 #include "ImportCmd.h"
 #include "PlayCmd.h"
@@ -62,10 +61,6 @@ Command* CommandParser::Parse(const Vector<String>& arguments)
             {
                 cmd = new BuildCmd(context_);
             }
-            else if (argument == "platform-add")
-            {
-                cmd = new PlatformAddCmd(context_);
-            }
             else if (argument == "import")
             {
                 cmd = new ImportCmd(context_);

+ 83 - 71
Source/ToolCore/Command/NETCmd.cpp

@@ -32,6 +32,10 @@
 
 #include "../ToolSystem.h"
 #include "../ToolEnvironment.h"
+#include "../Project/Project.h"
+#include "../Project/ProjectSettings.h"
+
+#include "../Build/BuildSystem.h"
 
 #include "NETCmd.h"
 #include "../NETTools/AtomicNETService.h"
@@ -41,7 +45,8 @@
 namespace ToolCore
 {
 
-NETCmd::NETCmd(Context* context) : Command(context)
+NETCmd::NETCmd(Context* context) : Command(context),
+	requiresProjectLoad_(false)
 {
 
 }
@@ -62,46 +67,7 @@ bool NETCmd::Parse(const Vector<String>& arguments, unsigned startIndex, String&
         return false;
     }
 
-    if (command_ == "genproject")
-    {
-        projectFile_ = startIndex + 2 < arguments.Size() ? arguments[startIndex + 2] : String::EMPTY;
-        scriptPlatform_ = startIndex + 3 < arguments.Size() ? arguments[startIndex + 3] : String::EMPTY;
-
-        if (!projectFile_.Length())
-        {
-            errorMsg = "Unable to parse project file";
-            return false;
-        }
-
-
-        if (!scriptPlatform_.Length())
-        {
-            errorMsg = "Unable to parse script platform";
-            return false;
-        }
-            
-    }
-    else if (command_ == "parse")
-    {
-        assemblyPath_ = startIndex + 2 < arguments.Size() ? arguments[startIndex + 2] : String::EMPTY;
-
-        bool exists = false;
-
-        if (assemblyPath_.Length())
-        {
-            FileSystem* fs = GetSubsystem<FileSystem>();
-            exists = fs->FileExists(assemblyPath_);
-        }
-
-        if (!exists)
-        {
-            errorMsg = ToString("Assembly file not found: %s", assemblyPath_.CString());
-            return false;
-        }
-
-        return true;
-    }
-    else if (command_ == "compile")
+    if (command_ == "compile")
     {
         solutionPath_ = startIndex + 2 < arguments.Size() ? arguments[startIndex + 2] : String::EMPTY;
         platform_ = startIndex + 3 < arguments.Size() ? arguments[startIndex + 3] : String::EMPTY;
@@ -129,6 +95,12 @@ bool NETCmd::Parse(const Vector<String>& arguments, unsigned startIndex, String&
 
         return true;
     }
+	else if (command_ == "genresources")
+	{
+		projectPath_ = startIndex + 2 < arguments.Size() ? arguments[startIndex + 2] : String::EMPTY;
+		platform_ = startIndex + 3 < arguments.Size() ? arguments[startIndex + 3] : String::EMPTY;
+		requiresProjectLoad_ = true;
+	}
     else
     {
         errorMsg = "Unknown net command";
@@ -161,28 +133,47 @@ void NETCmd::HandleNETBuildResult(StringHash eventType, VariantMap& eventData)
 
 void NETCmd::Run()
 {
-    if (command_ == "parse")
-    {
-        // start the NETService, which means we also need IPC
-        IPC* ipc = new IPC(context_);
-        context_->RegisterSubsystem(ipc);
-
-        netService_ = new AtomicNETService(context_);
-        context_->RegisterSubsystem(netService_);
-
-        if (!netService_->Start())
-        {
-            Error("Unable to start AtomicNETService");
-            Finished();
-        }
-    }
-    else if (command_ == "compile")
+    if (command_ == "compile")
     {
+		
+		FileSystem* fileSystem = GetSubsystem<FileSystem>();
 
         NETBuildSystem* buildSystem = new NETBuildSystem(context_);
         context_->RegisterSubsystem(buildSystem);
-       
-        NETBuild* build = buildSystem->Build(solutionPath_, platform_, configuration_);
+
+		NETBuild* build = 0;
+
+		String solutionPath;
+		String fileName;
+		String ext;
+
+		// detect project        
+		SplitPath(solutionPath_, solutionPath, fileName, ext);
+
+		if (ext == ".atomic")
+		{
+			SharedPtr<NETProjectGen> gen(new NETProjectGen(context_));
+
+			if (!gen->LoadAtomicProject(solutionPath_))
+			{
+				Error(ToString("NETProjectGen: Error loading project (%s)", solutionPath.CString()));
+				Finished();
+				return;
+			}
+
+			if (!gen->Generate())
+			{
+				Error(ToString("NETProjectGen: Error generating project (%s)", solutionPath.CString()));
+				Finished();
+				return;
+			}
+
+			solutionPath_ = solutionPath + "/AtomicNET/Solution/" + gen->GetProjectSettings()->GetName() + ".sln";
+
+		}
+
+		// json project file
+		build = buildSystem->Build(solutionPath_, platform_, configuration_);
 
         if (!build)
         {
@@ -194,18 +185,39 @@ void NETCmd::Run()
         build->SubscribeToEvent(E_NETBUILDRESULT, ATOMIC_HANDLER(NETCmd, HandleNETBuildResult));
 
     }
-    else if (command_ == "genproject")
-    {
-        SharedPtr<NETProjectGen> gen(new NETProjectGen(context_));
-
-        gen->SetScriptPlatform(scriptPlatform_);
-        gen->LoadProject(projectFile_);
-
-        gen->Generate();
-
-        Finished();
-
-    }
+	else if (command_ == "genresources")
+	{
+		BuildSystem* buildSystem = GetSubsystem<BuildSystem>();
+		ToolSystem* toolSystem = GetSubsystem<ToolSystem>();
+		Project* project = toolSystem->GetProject();
+
+		if (!project)
+		{
+			Error("Unable to get project");
+			Finished();
+			return;
+		}
+
+		buildSystem->SetBuildPath(project->GetProjectPath() + "AtomicNET/Resources/");
+
+		Platform* platform = toolSystem->GetPlatformByName(platform_);
+
+		if (!platform)
+		{
+			Error(ToString("Unknown platform %s", platform_.CString()));
+			Finished();
+			return;
+		}
+
+		BuildBase* buildBase = platform->NewBuild(project);
+		buildBase->SetResourcesOnly(true);
+		buildBase->SetVerbose(true);
+		buildSystem->QueueBuild(buildBase);
+		buildSystem->StartNextBuild();
+
+		Finished();
+		return;
+	}
 
 }
 

+ 7 - 4
Source/ToolCore/Command/NETCmd.h

@@ -46,7 +46,9 @@ public:
 
     void Run();
 
-    bool RequiresProjectLoad() { return false; }
+    bool RequiresProjectLoad() { return requiresProjectLoad_; }
+
+	const String& GetProjectPath() const { return projectPath_; }
 
 private:
 
@@ -54,9 +56,8 @@ private:
 
     String command_;
 
-    // genproject command
-    String projectFile_;
-    String scriptPlatform_;
+    // genresources command
+    String projectPath_;
 
     // parse command
     String assemblyPath_;
@@ -66,6 +67,8 @@ private:
     String platform_;
     String configuration_;
 
+	bool requiresProjectLoad_;
+
     WeakPtr<AtomicNETService> netService_;
 
 };

+ 0 - 95
Source/ToolCore/Command/PlatformAddCmd.cpp

@@ -1,95 +0,0 @@
-//
-// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-
-#include <Atomic/Core/StringUtils.h>
-#include <Atomic/IO/Log.h>
-#include <Atomic/IO/File.h>
-
-#include "../ToolSystem.h"
-#include "../Project/Project.h"
-
-#include "PlatformAddCmd.h"
-
-#include <Poco/File.h>
-
-namespace ToolCore
-{
-
-PlatformAddCmd::PlatformAddCmd(Context* context) : Command(context)
-{
-
-}
-
-PlatformAddCmd::~PlatformAddCmd()
-{
-
-}
-
-bool PlatformAddCmd::Parse(const Vector<String>& arguments, unsigned startIndex, String& errorMsg)
-{
-    String argument = arguments[startIndex].ToLower();
-    String value = startIndex + 1 < arguments.Size() ? arguments[startIndex + 1] : String::EMPTY;
-
-    if (argument != "platform-add")
-    {
-        errorMsg = "Unable to parse build command";
-        return false;
-    }
-
-    if (!value.Length())
-    {
-        errorMsg = "Unable to parse platform";
-        return false;
-    }
-
-    platformToAdd_ = value.ToLower();
-
-    return true;
-}
-
-void PlatformAddCmd::Run()
-{
-    ToolSystem* tsystem = GetSubsystem<ToolSystem>();
-    Project* project = tsystem->GetProject();
-
-    Platform* platform = tsystem->GetPlatformByName(platformToAdd_);
-
-    if (!platform)
-    {
-        Error(ToString("Unknown platform: %s", platformToAdd_.CString()));
-        return;
-    }
-
-    if (project->ContainsPlatform(platform->GetPlatformID()))
-    {
-        Error(ToString("Project already contains platform: %s", platformToAdd_.CString()));
-        return;
-    }
-
-    ATOMIC_LOGINFOF("Adding platform: %s", platformToAdd_.CString());
-
-    project->AddPlatform(platform->GetPlatformID());
-
-    Finished();
-}
-
-}

+ 0 - 51
Source/ToolCore/Command/PlatformAddCmd.h

@@ -1,51 +0,0 @@
-//
-// Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-
-#pragma once
-
-#include "Command.h"
-
-using namespace Atomic;
-
-namespace ToolCore
-{
-
-class PlatformAddCmd: public Command
-{
-    ATOMIC_OBJECT(PlatformAddCmd, Command);
-
-public:
-
-    PlatformAddCmd(Context* context);
-    virtual ~PlatformAddCmd();
-
-    bool Parse(const Vector<String>& arguments, unsigned startIndex, String& errorMsg);
-
-    void Run();
-
-private:
-
-    String platformToAdd_;
-
-};
-
-}

+ 19 - 81
Source/ToolCore/JSBind/CSharp/CSFunctionWriter.cpp

@@ -32,87 +32,8 @@
 #include "CSTypeHelper.h"
 #include "CSFunctionWriter.h"
 
-/*
- *
- C# getters/setters
- local instance storage so we're not constantly creating managed Vector3, etc
- Vector2/Vector3/BoundingBox, etc C# structs so assign by value
- Object lifetime
-
- C# enum of module types for type info?
- C# version of push class instance?
-
- new instance from C# needs constructor
- wrapping does not, wrapping doesn't use constructors at all (JS needs this for prototype)
-
- Store GCHandle to keep an object alive (Component, UI) C# side?
-
- typedef const void* ClassID;
- which changed based on address, so need register at startup
- so at package startup time, need to setup mapping between
- IntPtr and C# class, we also need to be able to new a class
- instance with existing native or create a native when new'ing from C#
-
- IntPtr to RefCounted native side is the "ID", like JSHeapPtr
-
- Lifetime:
-
- // you cannot derive from native engine classes, other than script components
-
- a C# instance can be new'd, handed to native, stored in native, the C# side could be GC'd
- future access to this instance would be a new instance
-
-*/
-
-
-/*
-
-// struct marshal Vector2, Vector3, BoundingBox, etc
-// RefCounted*
-// primitive bool, int, uint, float, double
-// String
-
-RefCounted* csb_Node_Constructor()
-{
-    return new Node(NETCore::GetContext());
-}
-
-void csb_Node_GetPosition(Node* self, Vector3* out)
-{
-    *out = self->GetPosition();
-}
-
-void csb_Node_SetPosition(Node* self, Vector3*__arg0)
-{
-    self->SetPosition(*__arg0);
-}
-
-void csb_Node_SetPosition(Node* self, Vector3*__arg0)
-{
-    self->SetPosition(*__arg0);
-}
-
-bool csb_Audio_Play(Audio* self)
-{
-    bool retValue = self->Play();
-    return retValue;
-}
-
-const RefCounted* csb_Node_GetParent(Node* self)
-{
-    const RefCounted* retValue = self->GetParent();
-    return RefCounted;
-}
-
-RefCounted* csb_ObjectAnimation_Constructor()
-{
-    return new ObjectAnimation(NETCore::GetContext());
-}
-
-*/
 namespace ToolCore
 {
-
 bool CSFunctionWriter::wroteConstructor_ = false;
 
 CSFunctionWriter::CSFunctionWriter(JSBFunction *function) : JSBFunctionWriter(function)
@@ -550,6 +471,7 @@ void CSFunctionWriter::GenManagedFunctionParameters(String& sig)
     {
         for (unsigned int i = 0; i < parameters.Size(); i++)
         {
+			bool isStruct = false;
             JSBFunctionType* ptype = parameters.At(i);
 
             // ignore "Context" parameters
@@ -557,13 +479,29 @@ void CSFunctionWriter::GenManagedFunctionParameters(String& sig)
             {
                 JSBClassType* classType = ptype->type_->asClassType();
                 JSBClass* klass = classType->class_;
+
                 if (klass->GetName() == "Context")
                 {
                     continue;
                 }
+
+				// TODO: we should have a better system for struct type in general
+				// This number array is really for JS
+				if (klass->IsNumberArray())
+				{
+					isStruct = true;
+				}
             }
 
-            sig += CSTypeHelper::GetManagedTypeString(ptype);
+			String managedTypeString = CSTypeHelper::GetManagedTypeString(ptype);
+
+			if (!ptype->isConst_ && (ptype->isReference_ && isStruct))
+			{
+				// pass by reference
+				managedTypeString = "ref " + managedTypeString;
+			}
+
+			sig += managedTypeString;
 
             String init = ptype->initializer_;
 
@@ -622,7 +560,7 @@ void CSFunctionWriter::WriteManagedConstructor(String& source)
 
     Indent();
 
-    source += IndentLine(ToString("var classType = typeof(%s);\n", klass->GetName().CString()));
+	source += IndentLine(ToString("var classType = typeof(%s);\n", klass->GetName().CString()));
     source += IndentLine("var thisType = this.GetType();\n");
     source += IndentLine("var thisTypeIsNative = NativeCore.IsNativeType(thisType);\n");    
     source += IndentLine("var nativeAncsestorType = NativeCore.GetNativeAncestorType(thisType);\n");

+ 40 - 1
Source/ToolCore/JSBind/CSharp/CSModuleWriter.cpp

@@ -102,6 +102,13 @@ void CSModuleWriter::GenerateNativeSource()
 {
     String source = "// This file was autogenerated by JSBind, changes will be lost\n";
 
+	String moduleGuard = module_->GetModuleDefineGuard();
+
+	if (moduleGuard.Length())
+	{
+		source += ToString("\n%s\n", moduleGuard.CString());
+	}
+
     source += "#ifdef ATOMIC_PLATFORM_WINDOWS\n";
 
     source += "#pragma warning(disable: 4244) // possible loss of data\n";
@@ -139,8 +146,22 @@ void CSModuleWriter::GenerateNativeSource()
 
     for (unsigned i = 0; i < classes.Size(); i++)
     {
+
+		String classGuard = module_->GetClassDefineGuard(classes[i]->GetNativeName());
+
+		if (classGuard.Length())
+		{
+			source += ToString("\n%s\n\n", classGuard.CString());
+		}
+
         CSClassWriter clsWriter(classes[i]);
         clsWriter.GenerateNativeSource(source);
+
+		if (classGuard.Length())
+		{
+			source += "#endif\n\n";
+		}
+
     }
 
     source += "// End Classes\n\n";
@@ -153,6 +174,11 @@ void CSModuleWriter::GenerateNativeSource()
         source += "#endif //ATOMIC_3D\n";
     }
 
+	if (moduleGuard.Length())
+	{
+		source += "\n#endif\n";
+	}
+
     JSBind* jsbind = module_->GetSubsystem<JSBind>();
 
     String filepath = jsbind->GetDestNativeFolder() + "/CSModule" + module_->name_ + ".cpp";
@@ -390,11 +416,24 @@ void CSModuleWriter::GenerateManagedModuleClass(String& sourceOut)
 
         const char* className = klass->GetName().CString();
 
+		String classGuard = module_->GetClassDefineGuard(classes[i]->GetNativeName(), "csharp");
+
+		if (classGuard.Length())
+		{
+			source += ToString("\n%s\n", classGuard.CString());
+		}
+
         line = ToString("new NativeType(%s.csb_%s_%s_GetClassIDStatic (), ", className, package->GetName().CString(), className);
         line += ToString("typeof(%s), (IntPtr x) => { return new %s (x); } );\n",className, className);
 
         source += IndentLine(line);
 
+		if (classGuard.Length())
+		{
+			source += ToString("#endif\n", classGuard.CString());
+		}
+
+
     }
 
     Dedent();
@@ -416,7 +455,7 @@ void CSModuleWriter::GenerateManagedSource()
 
     String moduleName = module_->GetPackage()->GetNamespace();
 
-    source += "\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.InteropServices;\n";
+    source += "\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.InteropServices;\nusing static System.Reflection.IntrospectionExtensions;\n";
 
     if (moduleName == "Atomic")
         moduleName = "AtomicEngine";

+ 12 - 3
Source/ToolCore/JSBind/CSharp/CSPackageWriter.cpp

@@ -171,6 +171,13 @@ void CSPackageWriter::GenerateNativeSource()
 
     String source = "// This file was autogenerated by AtomicTool, changes will be lost\n\n";
 
+	String defineGuard = package_->GetPlatformDefineGuard();
+
+	if (defineGuard.Length())
+	{
+		source += ToString("%s\n\n", defineGuard.CString());
+	}
+
     const char* packageName = package_->name_.CString();
 
     String packageHeader = "CSPackage" + package_->name_ + ".h";
@@ -180,14 +187,16 @@ void CSPackageWriter::GenerateNativeSource()
     if (package_->name_ != "Atomic")
         source += "using namespace Atomic;\n";
 
-    // begin namespace
-    // source += ToString("using namespace %s;\n", packageName);
-
     source += ToString("namespace %s\n{\n", packageName);
 
     // end of namespace
     source += "\n}\n";
 
+	if (defineGuard.Length())
+	{
+		source += "\n#endif\n";
+	}
+
     JSBind* jsbind = package_->GetSubsystem<JSBind>();
 
     String filepath = jsbind->GetDestNativeFolder() + "/CSPackage" + package_->name_ + ".cpp";

+ 3 - 2
Source/ToolCore/JSBind/JSBEnum.cpp

@@ -54,6 +54,7 @@ void JSBEnum::Preprocess()
 
     JSBind* jsbind = GetSubsystem<JSBind>();
 
+	// FIXME: This is going to need to be hand rolled with unified script bindings
     if (name_ == "TextureUnit")
     {
         values_.Clear();
@@ -66,9 +67,9 @@ void JSBEnum::Preprocess()
         values_["TU_EMISSIVE"] = "3";
         values_["TU_ENVIRONMENT"] = "4";
 
-        String platform = jsbind->GetPlatform();
+        // String platform = jsbind->GetPlatform();
 
-        bool mobile = platform == "WEB" || platform == "ANDROID" || platform == "IOS";
+		bool mobile = true; //  platform == "WEB" || platform == "ANDROID" || platform == "IOS";
 
         if (mobile)
         {

+ 1 - 1
Source/ToolCore/JSBind/JSBHaxe.cpp

@@ -115,7 +115,7 @@ namespace ToolCore
     }
 
     bool JSBHaxe::IsOverride(JSBFunction* function) {
-        return findFunctionInBase(function);
+        return findFunctionInBase(function) != NULL;
     }
 
     JSBFunction* JSBHaxe::findFunctionInBase(JSBFunction* function) {

Some files were not shown because too many files changed in this diff