Add Mac platform

This commit is contained in:
Wojciech Figat
2021-12-27 14:04:16 +01:00
parent 36a18a0b56
commit d017dd14ca
62 changed files with 2384 additions and 55 deletions

View File

@@ -42,6 +42,7 @@ namespace FlaxEditor.Content.Create
SwitchPlatformSettings, SwitchPlatformSettings,
[EditorDisplay(null, "PS5 Platform Settings")] [EditorDisplay(null, "PS5 Platform Settings")]
PS5PlatformSettings, PS5PlatformSettings,
MacPlatformSettings,
} }
private static readonly Type[] _types = private static readonly Type[] _types =
@@ -66,6 +67,7 @@ namespace FlaxEditor.Content.Create
typeof(AndroidPlatformSettings), typeof(AndroidPlatformSettings),
TypeUtils.GetManagedType(GameSettings.SwitchPlatformSettingsTypename), TypeUtils.GetManagedType(GameSettings.SwitchPlatformSettingsTypename),
TypeUtils.GetManagedType(GameSettings.PS5PlatformSettingsTypename), TypeUtils.GetManagedType(GameSettings.PS5PlatformSettingsTypename),
typeof(MacPlatformSettings),
}; };
internal class Options internal class Options
@@ -236,6 +238,11 @@ namespace FlaxEditor.Content.Create
return false; return false;
instance.PS5Platform = asset; instance.PS5Platform = asset;
break; break;
case SettingsTypes.MacPlatformSettings:
if (instance.MacPlatform != null)
return false;
instance.MacPlatform = asset;
break;
} }
return true; return true;
} }

View File

@@ -99,6 +99,12 @@ API_ENUM() enum class BuildPlatform
/// </summary> /// </summary>
API_ENUM(Attributes="EditorDisplay(null, \"PlayStation 5\")") API_ENUM(Attributes="EditorDisplay(null, \"PlayStation 5\")")
PS5 = 11, PS5 = 11,
/// <summary>
/// MacOS (x86-64 Intel)
/// </summary>
API_ENUM(Attributes="EditorDisplay(null, \"Mac x64\")")
MacOSx64 = 12,
}; };
extern FLAXENGINE_API const Char* ToString(const BuildPlatform platform); extern FLAXENGINE_API const Char* ToString(const BuildPlatform platform);

View File

@@ -58,6 +58,10 @@
#if PLATFORM_TOOLS_SWITCH #if PLATFORM_TOOLS_SWITCH
#include "Platforms/Switch/Editor/PlatformTools/SwitchPlatformTools.h" #include "Platforms/Switch/Editor/PlatformTools/SwitchPlatformTools.h"
#endif #endif
#if PLATFORM_TOOLS_MAC
#include "Platform/Mac/MacPlatformTools.h"
#include "Engine/Platform/Mac/MacPlatformSettings.h"
#endif
namespace GameCookerImpl namespace GameCookerImpl
{ {
@@ -129,6 +133,8 @@ const Char* ToString(const BuildPlatform platform)
return TEXT("Switch"); return TEXT("Switch");
case BuildPlatform::PS5: case BuildPlatform::PS5:
return TEXT("PlayStation 5"); return TEXT("PlayStation 5");
case BuildPlatform::MacOSx64:
return TEXT("Mac x64");
default: default:
return TEXT("?"); return TEXT("?");
} }
@@ -325,6 +331,11 @@ PlatformTools* GameCooker::GetTools(BuildPlatform platform)
case BuildPlatform::PS5: case BuildPlatform::PS5:
result = New<PS5PlatformTools>(); result = New<PS5PlatformTools>();
break; break;
#endif
#if PLATFORM_TOOLS_MAC
case BuildPlatform::MacOSx64:
result = New<MacPlatformTools>(ArchitectureType::x64);
break;
#endif #endif
} }
Tools.Add(platform, result); Tools.Add(platform, result);

View File

@@ -103,6 +103,7 @@ namespace FlaxEditor
case BuildPlatform.AndroidARM64: return PlatformType.Android; case BuildPlatform.AndroidARM64: return PlatformType.Android;
case BuildPlatform.XboxScarlett: return PlatformType.XboxScarlett; case BuildPlatform.XboxScarlett: return PlatformType.XboxScarlett;
case BuildPlatform.Switch: return PlatformType.Switch; case BuildPlatform.Switch: return PlatformType.Switch;
case BuildPlatform.MacOSx64: return PlatformType.Mac;
default: throw new ArgumentOutOfRangeException(nameof(buildPlatform), buildPlatform, null); default: throw new ArgumentOutOfRangeException(nameof(buildPlatform), buildPlatform, null);
} }
} }

View File

@@ -186,6 +186,10 @@ bool CompileScriptsStep::Perform(CookingData& data)
platform = TEXT("PS5"); platform = TEXT("PS5");
architecture = TEXT("x64"); architecture = TEXT("x64");
break; break;
case BuildPlatform::MacOSx64:
platform = TEXT("Mac");
architecture = TEXT("x64");
break;
default: default:
LOG(Error, "Unknown or unsupported build platform."); LOG(Error, "Unknown or unsupported build platform.");
return true; return true;

View File

@@ -61,8 +61,16 @@ public class Editor : EditorModule
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "XboxScarlett", "PLATFORM_TOOLS_XBOX_SCARLETT", "PLATFORM_TOOLS_GDK"); AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "XboxScarlett", "PLATFORM_TOOLS_XBOX_SCARLETT", "PLATFORM_TOOLS_GDK");
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Android", "PLATFORM_TOOLS_ANDROID"); AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Android", "PLATFORM_TOOLS_ANDROID");
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Switch", "PLATFORM_TOOLS_SWITCH"); AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Switch", "PLATFORM_TOOLS_SWITCH");
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Linux", "PLATFORM_TOOLS_LINUX");
}
else if (options.Platform.Target == TargetPlatform.Linux)
{
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Linux", "PLATFORM_TOOLS_LINUX");
}
else if (options.Platform.Target == TargetPlatform.Mac)
{
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Mac", "PLATFORM_TOOLS_MAC");
} }
AddPlatformTools(options, platformToolsRoot, platformToolsRootExternal, "Linux", "PLATFORM_TOOLS_LINUX");
// Visual Studio integration // Visual Studio integration
if (options.Platform.Target == TargetPlatform.Windows && Flax.Build.Platform.BuildTargetPlatform == TargetPlatform.Windows) if (options.Platform.Target == TargetPlatform.Windows && Flax.Build.Platform.BuildTargetPlatform == TargetPlatform.Windows)

View File

@@ -91,6 +91,7 @@ namespace FlaxEditor.GUI
new PlatformData(PlatformType.Android, icons.AndroidIcon128, "Android"), new PlatformData(PlatformType.Android, icons.AndroidIcon128, "Android"),
new PlatformData(PlatformType.Switch, icons.SwitchIcon128, "Switch"), new PlatformData(PlatformType.Switch, icons.SwitchIcon128, "Switch"),
new PlatformData(PlatformType.PS5, icons.PS5Icon128, "PlayStation 5"), new PlatformData(PlatformType.PS5, icons.PS5Icon128, "PlayStation 5"),
new PlatformData(PlatformType.Mac, icons.Flax64, "Mac"),
}; };
const float IconSize = 64.0f; const float IconSize = 64.0f;

View File

@@ -945,6 +945,7 @@ namespace FlaxEditor.Modules
Proxy.Add(new SettingsProxy(typeof(UWPPlatformSettings), Editor.Instance.Icons.UWPSettings128)); Proxy.Add(new SettingsProxy(typeof(UWPPlatformSettings), Editor.Instance.Icons.UWPSettings128));
Proxy.Add(new SettingsProxy(typeof(LinuxPlatformSettings), Editor.Instance.Icons.LinuxSettings128)); Proxy.Add(new SettingsProxy(typeof(LinuxPlatformSettings), Editor.Instance.Icons.LinuxSettings128));
Proxy.Add(new SettingsProxy(typeof(AndroidPlatformSettings), Editor.Instance.Icons.AndroidSettings128)); Proxy.Add(new SettingsProxy(typeof(AndroidPlatformSettings), Editor.Instance.Icons.AndroidSettings128));
Proxy.Add(new SettingsProxy(typeof(MacPlatformSettings), Editor.Instance.Icons.Document128));
var typePS4PlatformSettings = TypeUtils.GetManagedType(GameSettings.PS4PlatformSettingsTypename); var typePS4PlatformSettings = TypeUtils.GetManagedType(GameSettings.PS4PlatformSettingsTypename);
if (typePS4PlatformSettings != null) if (typePS4PlatformSettings != null)

View File

@@ -1342,13 +1342,13 @@ namespace FlaxEditor.Surface.Archetypes
}, },
new NodeArchetype new NodeArchetype
{ {
TypeID = 17, TypeID = 18,
Title = "Platform Switch", Title = "Platform Switch",
Description = "Gets the input value based on the runtime-platform type", Description = "Gets the input value based on the runtime-platform type",
Flags = NodeFlags.AllGraphs, Flags = NodeFlags.AllGraphs,
Size = new Vector2(220, 200), Size = new Vector2(220, 200),
ConnectionsHints = ConnectionsHint.Value, ConnectionsHints = ConnectionsHint.Value,
IndependentBoxes = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, IndependentBoxes = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 },
DependentBoxes = new[] { 0 }, DependentBoxes = new[] { 0 },
Elements = new[] Elements = new[]
{ {
@@ -1363,6 +1363,7 @@ namespace FlaxEditor.Surface.Archetypes
NodeElementArchetype.Factory.Input(7, "Android", true, null, 8), NodeElementArchetype.Factory.Input(7, "Android", true, null, 8),
NodeElementArchetype.Factory.Input(8, "Switch", true, null, 9), NodeElementArchetype.Factory.Input(8, "Switch", true, null, 9),
NodeElementArchetype.Factory.Input(9, "PlayStation 5", true, null, 10), NodeElementArchetype.Factory.Input(9, "PlayStation 5", true, null, 10),
NodeElementArchetype.Factory.Input(10, "Mac", true, null, 11),
} }
}, },
new NodeArchetype new NodeArchetype

View File

@@ -45,6 +45,7 @@ namespace FlaxEditor.Windows
{ PlatformType.Android, new Android() }, { PlatformType.Android, new Android() },
{ PlatformType.Switch, new Switch() }, { PlatformType.Switch, new Switch() },
{ PlatformType.PS5, new PS5() }, { PlatformType.PS5, new PS5() },
{ PlatformType.Mac, new Mac() },
}; };
public BuildTabProxy(GameCookerWindow win, PlatformSelector platformSelector) public BuildTabProxy(GameCookerWindow win, PlatformSelector platformSelector)
@@ -61,6 +62,7 @@ namespace FlaxEditor.Windows
PerPlatformOptions[PlatformType.Android].Init("Output/Android", "Android"); PerPlatformOptions[PlatformType.Android].Init("Output/Android", "Android");
PerPlatformOptions[PlatformType.Switch].Init("Output/Switch", "Switch"); PerPlatformOptions[PlatformType.Switch].Init("Output/Switch", "Switch");
PerPlatformOptions[PlatformType.PS5].Init("Output/PS5", "PS5"); PerPlatformOptions[PlatformType.PS5].Init("Output/PS5", "PS5");
PerPlatformOptions[PlatformType.Mac].Init("Output/Mac", "Mac");
} }
abstract class Platform abstract class Platform
@@ -102,7 +104,15 @@ namespace FlaxEditor.Windows
// Check if can build on that platform // Check if can build on that platform
#if PLATFORM_WINDOWS #if PLATFORM_WINDOWS
IsSupported = true; switch (BuildPlatform)
{
case BuildPlatform.MacOSx64:
IsSupported = false;
break;
default:
IsSupported = true;
break;
}
#elif PLATFORM_LINUX #elif PLATFORM_LINUX
switch (BuildPlatform) switch (BuildPlatform)
{ {
@@ -202,6 +212,11 @@ namespace FlaxEditor.Windows
protected override BuildPlatform BuildPlatform => BuildPlatform.PS5; protected override BuildPlatform BuildPlatform => BuildPlatform.PS5;
} }
class Mac : Platform
{
protected override BuildPlatform BuildPlatform => BuildPlatform.MacOSx64;
}
class Editor : CustomEditor class Editor : CustomEditor
{ {
private PlatformType _platform; private PlatformType _platform;
@@ -249,6 +264,9 @@ namespace FlaxEditor.Windows
case PlatformType.PS5: case PlatformType.PS5:
name = "PlayStation 5"; name = "PlayStation 5";
break; break;
case PlatformType.Mac:
name = "Mac";
break;
default: default:
name = CustomEditorsUtil.GetPropertyNameUI(_platform.ToString()); name = CustomEditorsUtil.GetPropertyNameUI(_platform.ToString());
break; break;

View File

@@ -54,6 +54,9 @@ public class Audio : EngineModule
options.SourcePaths.Add(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "PS5", "Engine", "Audio")); options.SourcePaths.Add(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "PS5", "Engine", "Audio"));
options.CompileEnv.PreprocessorDefinitions.Add("AUDIO_API_PS5"); options.CompileEnv.PreprocessorDefinitions.Add("AUDIO_API_PS5");
break; break;
case TargetPlatform.Mac:
useNone = true; // TODO: Audio support on Mac
break;
default: throw new InvalidPlatformException(options.Platform.Target); default: throw new InvalidPlatformException(options.Platform.Target);
} }

View File

@@ -65,6 +65,8 @@ IMPLEMENT_SETTINGS_GETTER(XboxScarlettPlatformSettings, XboxScarlettPlatform);
IMPLEMENT_SETTINGS_GETTER(AndroidPlatformSettings, AndroidPlatform); IMPLEMENT_SETTINGS_GETTER(AndroidPlatformSettings, AndroidPlatform);
#elif PLATFORM_SWITCH #elif PLATFORM_SWITCH
IMPLEMENT_SETTINGS_GETTER(SwitchPlatformSettings, SwitchPlatform); IMPLEMENT_SETTINGS_GETTER(SwitchPlatformSettings, SwitchPlatform);
#elif PLATFORM_MAC
IMPLEMENT_SETTINGS_GETTER(MacPlatformSettings, MacPlatform);
#else #else
#error Unknown platform #error Unknown platform
#endif #endif
@@ -226,6 +228,7 @@ void GameSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* mo
DESERIALIZE(AndroidPlatform); DESERIALIZE(AndroidPlatform);
DESERIALIZE(SwitchPlatform); DESERIALIZE(SwitchPlatform);
DESERIALIZE(PS5Platform); DESERIALIZE(PS5Platform);
DESERIALIZE(MacPlatform);
} }
void LayersAndTagsSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) void LayersAndTagsSettings::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)

View File

@@ -177,6 +177,14 @@ namespace FlaxEditor.Content.Settings
public JsonAsset PS5Platform; public JsonAsset PS5Platform;
#endif #endif
#if FLAX_EDITOR || PLATFORM_MAC
/// <summary>
/// Reference to <see cref="MacPlatformSettings"/> asset. Used to apply configuration on Mac platform.
/// </summary>
[EditorOrder(2090), EditorDisplay("Platform Settings", "Mac"), AssetReference(typeof(MacPlatformSettings), true), Tooltip("Reference to Mac Platform Settings asset")]
public JsonAsset MacPlatform;
#endif
/// <summary> /// <summary>
/// Gets the absolute path to the game settings asset file. /// Gets the absolute path to the game settings asset file.
/// </summary> /// </summary>
@@ -295,6 +303,10 @@ namespace FlaxEditor.Content.Settings
if (type.FullName == PS5PlatformSettingsTypename) if (type.FullName == PS5PlatformSettingsTypename)
return LoadAsset(gameSettings.PS5Platform, PS5PlatformSettingsTypename) as T; return LoadAsset(gameSettings.PS5Platform, PS5PlatformSettingsTypename) as T;
#endif #endif
#if FLAX_EDITOR || PLATFORM_MAC
if (type == typeof(MacPlatformSettings))
return LoadAsset<MacPlatformSettings>(gameSettings.MacPlatform) as T;
#endif
if (gameSettings.CustomSettings != null) if (gameSettings.CustomSettings != null)
{ {
@@ -391,6 +403,8 @@ namespace FlaxEditor.Content.Settings
return SaveAsset(gameSettings, ref gameSettings.PS5Platform, obj); return SaveAsset(gameSettings, ref gameSettings.PS5Platform, obj);
if (type == typeof(AudioSettings)) if (type == typeof(AudioSettings))
return SaveAsset(gameSettings, ref gameSettings.Audio, obj); return SaveAsset(gameSettings, ref gameSettings.Audio, obj);
if (type == typeof(MacPlatformSettings))
return SaveAsset(gameSettings, ref gameSettings.MacPlatform, obj);
return true; return true;
} }

View File

@@ -82,6 +82,7 @@ public:
Guid AndroidPlatform; Guid AndroidPlatform;
Guid SwitchPlatform; Guid SwitchPlatform;
Guid PS5Platform; Guid PS5Platform;
Guid MacPlatform;
public: public:

View File

@@ -32,3 +32,6 @@
#if PLATFORM_PS5 #if PLATFORM_PS5
#include "Platforms/PS5/Engine/Platform/PS5PlatformSettings.h" #include "Platforms/PS5/Engine/Platform/PS5PlatformSettings.h"
#endif #endif
#if PLATFORM_MAC
#include "Engine/Platform/Mac/MacPlatformSettings.h"
#endif

View File

@@ -22,6 +22,8 @@
#include "Android/AndroidGame.h" #include "Android/AndroidGame.h"
#elif PLATFORM_SWITCH #elif PLATFORM_SWITCH
#include "Platforms/Switch/Engine/Engine/SwitchGame.h" #include "Platforms/Switch/Engine/Engine/SwitchGame.h"
#elif PLATFORM_MAC
#include "Mac/MacGame.h"
#else #else
#error Missing Game implementation! #error Missing Game implementation!
#endif #endif

View File

@@ -0,0 +1,19 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_MAC && !USE_EDITOR
#include "../Base/GameBase.h"
/// <summary>
/// The game class implementation for Mac platform.
/// </summary>
/// <seealso cref="Game" />
class MacGame : public GameBase
{
};
typedef MacGame Game;
#endif

View File

@@ -81,6 +81,9 @@ public class Graphics : EngineModule
case TargetPlatform.Switch: case TargetPlatform.Switch:
options.PrivateDependencies.Add("GraphicsDeviceVulkan"); options.PrivateDependencies.Add("GraphicsDeviceVulkan");
break; break;
case TargetPlatform.Mac:
options.PrivateDependencies.Add("GraphicsDeviceNull"); // TODO: Graphics support on Mac
break;
default: throw new InvalidPlatformException(options.Platform.Target); default: throw new InvalidPlatformException(options.Platform.Target);
} }

View File

@@ -0,0 +1,24 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#if PLATFORM_MAC
#include "Engine/Engine/Engine.h"
#include "Engine/Core/Types/StringBuilder.h"
int main(int argc, char* argv[])
{
// Join the arguments
StringBuilder args;
for (int i = 1; i < argc; i++)
{
args.Append(argv[i]);
if (i + 1 != argc)
args.Append(TEXT(' '));
}
args.Append(TEXT('\0'));
return Engine::Main(*args);
}
#endif

View File

@@ -72,6 +72,9 @@ public class Main : EngineModule
case TargetPlatform.Switch: case TargetPlatform.Switch:
options.SourcePaths.Add(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Switch", "Engine", "Main")); options.SourcePaths.Add(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Switch", "Engine", "Main"));
break; break;
case TargetPlatform.Mac:
options.SourcePaths.Add(Path.Combine(FolderPath, "Mac"));
break;
default: throw new InvalidPlatformException(options.Platform.Target); default: throw new InvalidPlatformException(options.Platform.Target);
} }
} }

View File

@@ -614,6 +614,8 @@ const Char* ToString(PlatformType type)
return TEXT("Switch"); return TEXT("Switch");
case PlatformType::PS5: case PlatformType::PS5:
return TEXT("PlayStation 5"); return TEXT("PlayStation 5");
case PlatformType::Mac:
return TEXT("Mac");
default: default:
return TEXT(""); return TEXT("");
} }

View File

@@ -2,21 +2,9 @@
#pragma once #pragma once
#if PLATFORM_WINDOWS #if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT
#include "Win32/Win32CriticalSection.h" #include "Win32/Win32CriticalSection.h"
#elif PLATFORM_UWP #elif PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_PS4 || PLATFORM_PS5
#include "Win32/Win32CriticalSection.h"
#elif PLATFORM_LINUX
#include "Unix/UnixCriticalSection.h"
#elif PLATFORM_PS4
#include "Unix/UnixCriticalSection.h"
#elif PLATFORM_PS5
#include "Unix/UnixCriticalSection.h"
#elif PLATFORM_XBOX_ONE
#include "Win32/Win32CriticalSection.h"
#elif PLATFORM_XBOX_SCARLETT
#include "Win32/Win32CriticalSection.h"
#elif PLATFORM_ANDROID
#include "Unix/UnixCriticalSection.h" #include "Unix/UnixCriticalSection.h"
#elif PLATFORM_SWITCH #elif PLATFORM_SWITCH
#include "Platforms/Switch/Engine/Platform/SwitchCriticalSection.h" #include "Platforms/Switch/Engine/Platform/SwitchCriticalSection.h"

View File

@@ -53,6 +53,11 @@ API_ENUM() enum class PlatformType
/// Running on PlayStation 5. /// Running on PlayStation 5.
/// </summary> /// </summary>
PS5 = 9, PS5 = 9,
/// <summary>
/// Running on Mac.
/// </summary>
Mac = 10,
}; };
/// <summary> /// <summary>
@@ -120,9 +125,6 @@ API_ENUM() enum class ArchitectureType
#if !defined(PLATFORM_MAC) #if !defined(PLATFORM_MAC)
#define PLATFORM_MAC 0 #define PLATFORM_MAC 0
#endif #endif
#if !defined(PLATFORM_OSX)
#define PLATFORM_OSX 0
#endif
#if !defined(PLATFORM_IOS) #if !defined(PLATFORM_IOS)
#define PLATFORM_IOS 0 #define PLATFORM_IOS 0
#endif #endif
@@ -148,6 +150,8 @@ API_ENUM() enum class ArchitectureType
#include "Android/AndroidDefines.h" #include "Android/AndroidDefines.h"
#elif PLATFORM_SWITCH #elif PLATFORM_SWITCH
#include "Platforms/Switch/Engine/Platform/SwitchDefines.h" #include "Platforms/Switch/Engine/Platform/SwitchDefines.h"
#elif PLATFORM_MAC
#include "Mac/MacDefines.h"
#else #else
#error Missing Defines implementation! #error Missing Defines implementation!
#endif #endif
@@ -189,7 +193,7 @@ API_ENUM() enum class ArchitectureType
#define PLATFORM_WINDOWS_FAMILY (PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT) #define PLATFORM_WINDOWS_FAMILY (PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT)
#define PLATFORM_MICROSOFT_FAMILY (PLATFORM_WINDOWS_FAMILY) #define PLATFORM_MICROSOFT_FAMILY (PLATFORM_WINDOWS_FAMILY)
#define PLATFORM_UNIX_FAMILY (PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_PS4 || PLATFORM_PS5) #define PLATFORM_UNIX_FAMILY (PLATFORM_LINUX || PLATFORM_ANDROID || PLATFORM_PS4 || PLATFORM_PS5)
#define PLATFORM_APPLE_FAMILY (PLATFORM_IOS || PLATFORM_OSX) #define PLATFORM_APPLE_FAMILY (PLATFORM_MAC || PLATFORM_IOS)
// SIMD defines // SIMD defines
#if defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) || defined(__SSE2__) #if defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) || defined(__SSE2__)

View File

@@ -2,24 +2,16 @@
#pragma once #pragma once
#if PLATFORM_WINDOWS #if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT
#include "Win32/Win32File.h" #include "Win32/Win32File.h"
#elif PLATFORM_UWP #elif PLATFORM_LINUX || PLATFORM_PS4 || PLATFORM_PS5
#include "Win32/Win32File.h"
#elif PLATFORM_LINUX
#include "Unix/UnixFile.h" #include "Unix/UnixFile.h"
#elif PLATFORM_PS4
#include "Unix/UnixFile.h"
#elif PLATFORM_PS5
#include "Unix/UnixFile.h"
#elif PLATFORM_XBOX_ONE
#include "Win32/Win32File.h"
#elif PLATFORM_XBOX_SCARLETT
#include "Win32/Win32File.h"
#elif PLATFORM_ANDROID #elif PLATFORM_ANDROID
#include "Android/AndroidFile.h" #include "Android/AndroidFile.h"
#elif PLATFORM_SWITCH #elif PLATFORM_SWITCH
#include "Platforms/Switch/Engine/Platform/SwitchFile.h" #include "Platforms/Switch/Engine/Platform/SwitchFile.h"
#elif PLATFORM_MAC
#include "Mac/MacFile.h"
#else #else
#error Missing File implementation! #error Missing File implementation!
#endif #endif

View File

@@ -20,6 +20,8 @@
#include "Android/AndroidFileSystem.h" #include "Android/AndroidFileSystem.h"
#elif PLATFORM_SWITCH #elif PLATFORM_SWITCH
#include "Platforms/Switch/Engine/Platform/SwitchFileSystem.h" #include "Platforms/Switch/Engine/Platform/SwitchFileSystem.h"
#elif PLATFORM_MAC
#include "Mac/MacFileSystem.h"
#else #else
#error Missing File System implementation! #error Missing File System implementation!
#endif #endif

View File

@@ -0,0 +1,91 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_MAC
#include "MacCriticalSection.h"
#include <pthread.h>
#include <sys/time.h>
/// <summary>
/// Mac implementation of a condition variables. Condition variables are synchronization primitives that enable threads to wait until a particular condition occurs. Condition variables enable threads to atomically release a lock and enter the sleeping state.
/// </summary>
class FLAXENGINE_API MacConditionVariable
{
private:
pthread_cond_t _cond;
private:
MacConditionVariable(const MacConditionVariable&);
MacConditionVariable& operator=(const MacConditionVariable&);
public:
/// <summary>
/// Initializes a new instance of the <see cref="MacConditionVariable"/> class.
/// </summary>
MacConditionVariable()
{
pthread_cond_init(&_cond, nullptr);
}
/// <summary>
/// Finalizes an instance of the <see cref="MacConditionVariable"/> class.
/// </summary>
~MacConditionVariable()
{
pthread_cond_destroy(&_cond);
}
public:
/// <summary>
/// Blocks the current thread execution until the condition variable is woken up.
/// </summary>
/// <param name="lock">The critical section locked by the current thread.</param>
void Wait(const MacCriticalSection& lock)
{
pthread_cond_wait(&_cond, lock._mutexPtr);
}
/// <summary>
/// Blocks the current thread execution until the condition variable is woken up or after the specified timeout duration.
/// </summary>
/// <param name="lock">The critical section locked by the current thread.</param>
/// <param name="timeout">The time-out interval, in milliseconds. If the time-out interval elapses, the function re-acquires the critical section and returns zero. If timeout is zero, the function tests the states of the specified objects and returns immediately. If timeout is INFINITE, the function's time-out interval never elapses.</param>
/// <returns>If the function succeeds, the return value is true, otherwise, if the function fails or the time-out interval elapses, the return value is false.</returns>
bool Wait(const MacCriticalSection& lock, const int32 timeout)
{
struct timeval tv;
struct timespec ts;
gettimeofday(&tv, NULL);
ts.tv_sec = time(NULL) + timeout / 1000;
ts.tv_nsec = tv.tv_usec * 1000 + 1000 * 1000 * (timeout % 1000);
ts.tv_sec += ts.tv_nsec / (1000 * 1000 * 1000);
ts.tv_nsec %= (1000 * 1000 * 1000);
return pthread_cond_timedwait(&_cond, lock._mutexPtr, &ts) == 0;
}
/// <summary>
/// Notifies one waiting thread.
/// </summary>
void NotifyOne()
{
pthread_cond_signal(&_cond);
}
/// <summary>
/// Notifies all waiting threads.
/// </summary>
void NotifyAll()
{
pthread_cond_broadcast(&_cond);
}
};
#endif

View File

@@ -0,0 +1,92 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_MAC
#include "Engine/Platform/Platform.h"
#include <pthread.h>
class MacConditionVariable;
/// <summary>
/// Mac implementation of a critical section.
/// </summary>
class FLAXENGINE_API MacCriticalSection
{
friend MacConditionVariable;
private:
pthread_mutex_t _mutex;
pthread_mutex_t* _mutexPtr;
#if BUILD_DEBUG
pthread_t _owningThreadId;
#endif
private:
MacCriticalSection(const MacCriticalSection&);
MacCriticalSection& operator=(const MacCriticalSection&);
public:
/// <summary>
/// Initializes a new instance of the <see cref="MacCriticalSection"/> class.
/// </summary>
MacCriticalSection()
{
pthread_mutexattr_t attributes;
pthread_mutexattr_init(&attributes);
pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&_mutex, &attributes);
pthread_mutexattr_destroy(&attributes);
_mutexPtr = &_mutex;
#if BUILD_DEBUG
_owningThreadId = 0;
#endif
}
/// <summary>
/// Finalizes an instance of the <see cref="MacCriticalSection"/> class.
/// </summary>
~MacCriticalSection()
{
pthread_mutex_destroy(&_mutex);
}
public:
/// <summary>
/// Locks the critical section.
/// </summary>
void Lock() const
{
pthread_mutex_lock(_mutexPtr);
#if BUILD_DEBUG
((MacCriticalSection*)this)->_owningThreadId = pthread_self();
#endif
}
/// <summary>
/// Attempts to enter a critical section without blocking. If the call is successful, the calling thread takes ownership of the critical section.
/// </summary>
/// <returns>True if calling thread took ownership of the critical section.</returns>
bool TryLock() const
{
return pthread_mutex_trylock(_mutexPtr) == 0;
}
/// <summary>
/// Releases the lock on the critical section.
/// </summary>
void Unlock() const
{
#if BUILD_DEBUG
((MacCriticalSection*)this)->_owningThreadId = 0;
#endif
pthread_mutex_unlock(_mutexPtr);
}
};
#endif

View File

@@ -0,0 +1,19 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_MAC
// Platform description
#define PLATFORM_64BITS 1
#define PLATFORM_ARCH_X64 1
#define PLATFORM_ARCH ArchitectureType::x64
#define PLATFORM_TYPE PlatformType::Mac
#define PLATFORM_DESKTOP 1
#define PLATFORM_CACHE_LINE_SIZE 128
#define PLATFORM_HAS_HEADLESS_MODE 1
#define PLATFORM_DEBUG_BREAK __builtin_trap()
#define PLATFORM_LINE_TERMINATOR "\n"
#define PLATFORM_TEXT_IS_CHAR16 1
#endif

View File

@@ -0,0 +1,156 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#if PLATFORM_MAC
#include "../File.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Types/String.h"
#include "Engine/Core/Types/DateTime.h"
#include "Engine/Core/Types/TimeSpan.h"
#include "Engine/Utilities/StringConverter.h"
#include "Engine/Core/Log.h"
#include <sys/file.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <cerrno>
MacFile::MacFile(int32 handle)
: _handle(handle)
{
}
MacFile::~MacFile()
{
Close();
}
MacFile* MacFile::Open(const StringView& path, FileMode mode, FileAccess access, FileShare share)
{
int flags = O_CLOEXEC;
switch (access)
{
case FileAccess::Read:
flags |= O_RDONLY;
break;
case FileAccess::Write:
flags |= O_WRONLY;
break;
case FileAccess::ReadWrite:
flags |= O_RDWR;
break;
default: ;
}
switch (mode)
{
case FileMode::CreateAlways:
flags |= O_CREAT | O_TRUNC;
break;
case FileMode::CreateNew:
flags |= O_CREAT | O_EXCL;
break;
case FileMode::OpenAlways:
break;
case FileMode::OpenExisting:
break;
case FileMode::TruncateExisting:
flags |= O_TRUNC;
break;
default: ;
}
mode_t omode = S_IRUSR | S_IWUSR;
if ((uint32)share & (uint32)FileShare::Delete)
omode |= 0;
if ((uint32)share & (uint32)FileShare::Read)
omode |= (mode_t)(S_IRGRP | S_IROTH);
if ((uint32)share & (uint32)FileShare::Write)
omode |= (mode_t)(S_IWGRP | S_IWOTH);
if ((uint32)share & (uint32)FileShare::Delete)
omode |= 0;
const StringAsANSI<> pathANSI(*path, path.Length());
auto handle = open(pathANSI.Get(), flags, omode);
if (handle == -1)
{
LOG_Mac_LAST_ERROR;
return nullptr;
}
return New<MacFile>(handle);
}
bool MacFile::Read(void* buffer, uint32 bytesToRead, uint32* bytesRead)
{
const ssize_t tmp = read(_handle, buffer, bytesToRead);
if (tmp != -1)
{
if (bytesRead)
*bytesRead = tmp;
return false;
}
if (bytesRead)
*bytesRead = 0;
LOG_Mac_LAST_ERROR;
return true;
}
bool MacFile::Write(const void* buffer, uint32 bytesToWrite, uint32* bytesWritten)
{
const ssize_t tmp = write(_handle, buffer, bytesToWrite);
if (tmp != -1)
{
if (bytesWritten)
*bytesWritten = tmp;
return false;
}
if (bytesWritten)
*bytesWritten = 0;
LOG_Mac_LAST_ERROR;
return true;
}
void MacFile::Close()
{
if (_handle != -1)
{
close(_handle);
_handle = -1;
}
}
uint32 MacFile::GetSize() const
{
struct stat fileInfo;
fstat(_handle, &fileInfo);
return fileInfo.st_size;
}
DateTime MacFile::GetLastWriteTime() const
{
struct stat fileInfo;
if (fstat(_handle, &fileInfo) == -1)
{
return DateTime::MinValue();
}
const TimeSpan timeSinceEpoch(0, 0, fileInfo.st_mtime);
const DateTime MacEpoch(1970, 1, 1);
return MacEpoch + timeSinceEpoch;
}
uint32 MacFile::GetPosition() const
{
return lseek(_handle, 0, SEEK_CUR);
}
void MacFile::SetPosition(uint32 seek)
{
lseek(_handle, seek, SEEK_SET);
}
bool MacFile::IsOpened() const
{
return _handle != -1;
}
#endif

View File

@@ -0,0 +1,56 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_MAC
#include "Engine/Platform/Base/FileBase.h"
/// <summary>
/// Mac platform file object implementation.
/// </summary>
class MacFile : public FileBase
{
protected:
int32 _handle;
public:
/// <summary>
/// Initializes a new instance of the <see cref="MacFile"/> class.
/// </summary>
/// <param name="handle">The handle.</param>
MacFile(int32 handle);
/// <summary>
/// Finalizes an instance of the <see cref="MacFile"/> class.
/// </summary>
~MacFile();
public:
/// <summary>
/// Creates or opens a file.
/// </summary>
/// <param name="path">The name of the file to be created or opened.</param>
/// <param name="mode">An action to take on a file that exists or does not exist.</param>
/// <param name="access">The requested access to the file.</param>
/// <param name="share">The requested sharing mode of the file.</param>
/// <returns>Opened file handle or null if cannot.</returns>
static MacFile* Open(const StringView& path, FileMode mode, FileAccess access = FileAccess::ReadWrite, FileShare share = FileShare::None);
public:
// [FileBase]
bool Read(void* buffer, uint32 bytesToRead, uint32* bytesRead = nullptr) override;
bool Write(const void* buffer, uint32 bytesToWrite, uint32* bytesWritten = nullptr) override;
void Close() override;
uint32 GetSize() const override;
DateTime GetLastWriteTime() const override;
uint32 GetPosition() const override;
void SetPosition(uint32 seek) override;
bool IsOpened() const override;
};
#endif

View File

@@ -0,0 +1,523 @@
// Copyright (c) 2012-2019 Wojciech Figat. All rights reserved.
#if PLATFORM_MAC
#include "MacFileSystem.h"
#include "Engine/Platform/File.h"
#include "Engine/Core/Types/String.h"
#include "Engine/Core/Types/StringView.h"
#include "Engine/Core/Types/TimeSpan.h"
#include "Engine/Core/Math/Math.h"
#include "Engine/Core/Log.h"
#include "Engine/Utilities/StringConverter.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <unistd.h>
#include <stdio.h>
#include <cerrno>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
const DateTime UnixEpoch(1970, 1, 1);
bool MacFileSystem::CreateDirectory(const StringView& path)
{
const StringAsANSI<> pathAnsi(*path, path.Length());
// Skip if already exists
struct stat fileInfo;
if (stat(pathAnsi.Get(), &fileInfo) != -1 && S_ISDIR(fileInfo.st_mode))
{
return false;
}
// Recursively do it all again for the parent directory, if any
const int32 slashIndex = path.FindLast('/');
if (slashIndex > 1)
{
if (CreateDirectory(path.Substring(0, slashIndex)))
{
return true;
}
}
// Create the last directory on the path (the recursive calls will have taken care of the parent directories by now)
return mkdir(pathAnsi.Get(), 0755) != 0 && errno != EEXIST;
}
bool DeletePathTree(const char* path)
{
size_t pathLength;
DIR* dir;
struct stat statPath, statEntry;
struct dirent* entry;
// Stat for the path
stat(path, &statPath);
// If path does not exists or is not dir - exit with status -1
if (S_ISDIR(statPath.st_mode) == 0)
{
// Is not directory
return true;
}
// If not possible to read the directory for this user
if ((dir = opendir(path)) == NULL)
{
// Cannot open directory
return true;
}
// The length of the path
pathLength = strlen(path);
// Iteration through entries in the directory
while ((entry = readdir(dir)) != NULL)
{
// Skip entries "." and ".."
if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
continue;
// Determinate a full path of an entry
char full_path[256];
ASSERT(pathLength + strlen(entry->d_name) < ARRAY_COUNT(full_path));
strcpy(full_path, path);
strcat(full_path, "/");
strcat(full_path, entry->d_name);
// Stat for the entry
stat(full_path, &statEntry);
// Recursively remove a nested directory
if (S_ISDIR(statEntry.st_mode) != 0)
{
if (DeletePathTree(full_path))
return true;
continue;
}
// Remove a file object
if (unlink(full_path) != 0)
return true;
}
// Remove the devastated directory and close the object of it
if (rmdir(path) != 0)
return true;
closedir(dir);
return false;
}
bool MacFileSystem::DeleteDirectory(const String& path, bool deleteContents)
{
const StringAsANSI<> pathANSI(*path, path.Length());
if (deleteContents)
return DeletePathTree(pathANSI.Get());
return rmdir(pathANSI.Get()) != 0;
}
bool MacFileSystem::DirectoryExists(const StringView& path)
{
struct stat fileInfo;
const StringAsANSI<> pathANSI(*path, path.Length());
if (stat(pathANSI.Get(), &fileInfo) != -1)
{
return S_ISDIR(fileInfo.st_mode);
}
return false;
}
bool MacFileSystem::DirectoryGetFiles(Array<String>& results, const String& path, const Char* searchPattern, DirectorySearchOption option)
{
const StringAsANSI<> pathANSI(*path, path.Length());
const StringAsANSI<> searchPatternANSI(searchPattern);
// Check if use only top directory
if (option == DirectorySearchOption::TopDirectoryOnly)
return getFilesFromDirectoryTop(results, pathANSI.Get(), searchPatternANSI.Get());
return getFilesFromDirectoryAll(results, pathANSI.Get(), searchPatternANSI.Get());
}
bool MacFileSystem::GetChildDirectories(Array<String>& results, const String& directory)
{
size_t pathLength;
DIR* dir;
struct stat statPath, statEntry;
struct dirent* entry;
const StringAsANSI<> pathANSI(*directory, directory.Length());
const char* path = pathANSI.Get();
// Stat for the path
stat(path, &statPath);
// If path does not exists or is not dir - exit with status -1
if (S_ISDIR(statPath.st_mode) == 0)
{
// Is not directory
return true;
}
// If not possible to read the directory for this user
if ((dir = opendir(path)) == NULL)
{
// Cannot open directory
return true;
}
// The length of the path
pathLength = strlen(path);
// Iteration through entries in the directory
while ((entry = readdir(dir)) != NULL)
{
// Skip entries "." and ".."
if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
continue;
// Determinate a full path of an entry
char full_path[256];
ASSERT(pathLength + strlen(entry->d_name) < ARRAY_COUNT(full_path));
strcpy(full_path, path);
strcat(full_path, "/");
strcat(full_path, entry->d_name);
// Stat for the entry
stat(full_path, &statEntry);
// Check for directory
if (S_ISDIR(statEntry.st_mode) != 0)
{
// Add directory
results.Add(String(full_path));
}
}
closedir(dir);
return false;
}
bool MacFileSystem::FileExists(const StringView& path)
{
struct stat fileInfo;
const StringAsANSI<> pathANSI(*path, path.Length());
if (stat(pathANSI.Get(), &fileInfo) != -1)
{
return S_ISREG(fileInfo.st_mode);
}
return false;
}
bool MacFileSystem::DeleteFile(const StringView& path)
{
const StringAsANSI<> pathANSI(*path, path.Length());
return unlink(pathANSI.Get()) == 0;
}
uint64 MacFileSystem::GetFileSize(const StringView& path)
{
struct stat fileInfo;
fileInfo.st_size = -1;
const StringAsANSI<> pathANSI(*path, path.Length());
if (stat(pathANSI.Get(), &fileInfo) != -1)
{
// Check for directories
if (S_ISDIR(fileInfo.st_mode))
{
fileInfo.st_size = -1;
}
}
return fileInfo.st_size;
}
bool MacFileSystem::IsReadOnly(const StringView& path)
{
const StringAsANSI<> pathANSI(*path, path.Length());
if (access(pathANSI.Get(), W_OK) == -1)
{
return errno == EACCES;
}
return false;
}
bool MacFileSystem::SetReadOnly(const StringView& path, bool isReadOnly)
{
const StringAsANSI<> pathANSI(*path, path.Length());
struct stat fileInfo;
if (stat(pathANSI.Get(), &fileInfo) != -1)
{
if (isReadOnly)
{
fileInfo.st_mode &= ~S_IWUSR;
}
else
{
fileInfo.st_mode |= S_IWUSR;
}
return chmod(pathANSI.Get(), fileInfo.st_mode) == 0;
}
return false;
}
bool MacFileSystem::MoveFile(const StringView& dst, const StringView& src, bool overwrite)
{
if (!overwrite && FileExists(dst))
{
// Already exists
return true;
}
if (overwrite)
{
unlink(StringAsANSI<>(*dst, dst.Length()).Get());
}
if (rename(StringAsANSI<>(*src, src.Length()).Get(), StringAsANSI<>(*dst, dst.Length()).Get()) != 0)
{
if (errno == EXDEV)
{
if (!CopyFile(dst, src))
{
unlink(StringAsANSI<>(*src, src.Length()).Get());
return false;
}
}
return true;
}
return false;
}
bool MacFileSystem::CopyFile(const StringView& dst, const StringView& src)
{
const StringAsANSI<> srcANSI(*src, src.Length());
const StringAsANSI<> dstANSI(*dst, dst.Length());
int srcFile, dstFile;
char buffer[4096];
ssize_t readSize;
int cachedError;
srcFile = open(srcANSI.Get(), O_RDONLY);
if (srcFile < 0)
return true;
dstFile = open(dstANSI.Get(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
if (dstFile < 0)
goto out_error;
// first try the kernel method
struct stat statBuf;
fstat(srcFile, &statBuf);
readSize = 1;
while (readSize > 0)
{
readSize = sendfile(dstFile, srcFile, 0, statBuf.st_size);
}
// sendfile could fail for example if the input file is not nmap'able
// in this case we fall back to the read/write loop
if (readSize < 0)
{
while (readSize = read(srcFile, buffer, sizeof(buffer)), readSize > 0)
{
char* ptr = buffer;
ssize_t writeSize;
do
{
writeSize = write(dstFile, ptr, readSize);
if (writeSize >= 0)
{
readSize -= writeSize;
ptr += writeSize;
}
else if (errno != EINTR)
{
goto out_error;
}
} while (readSize > 0);
}
}
if (readSize == 0)
{
if (close(dstFile) < 0)
{
dstFile = -1;
goto out_error;
}
close(srcFile);
// Success
return false;
}
out_error:
cachedError = errno;
close(srcFile);
if (dstFile >= 0)
close(dstFile);
errno = cachedError;
return true;
}
bool MacFileSystem::getFilesFromDirectoryTop(Array<String>& results, const char* path, const char* searchPattern)
{
size_t pathLength;
struct stat statPath, statEntry;
struct dirent* entry;
// Stat for the path
stat(path, &statPath);
// If path does not exists or is not dir - exit with status -1
if (S_ISDIR(statPath.st_mode) == 0)
{
// Is not directory
return true;
}
// If not possible to read the directory for this user
DIR* dir = opendir(path);
if (dir == NULL)
{
// Cannot open directory
return true;
}
// The length of the path
pathLength = strlen(path);
// Iteration through entries in the directory
while ((entry = readdir(dir)) != NULL)
{
// Skip entries "." and ".."
if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
continue;
// Determinate a full path of an entry
char fullPath[256];
ASSERT(pathLength + strlen(entry->d_name) < ARRAY_COUNT(fullPath));
strcpy(fullPath, path);
strcat(fullPath, "/");
strcat(fullPath, entry->d_name);
// Stat for the entry
stat(fullPath, &statEntry);
// Check for file
if (S_ISREG(statEntry.st_mode) != 0)
{
// Validate with filter
const int32 fullPathLength = StringUtils::Length(fullPath);
const int32 searchPatternLength = StringUtils::Length(searchPattern);
if (searchPatternLength == 0 || StringUtils::Compare(searchPattern, "*") == 0)
{
// All files
}
else if (searchPattern[0] == '*' && searchPatternLength < fullPathLength && StringUtils::Compare(fullPath + fullPathLength - searchPatternLength + 1, searchPattern + 1, searchPatternLength - 1) == 0)
{
// Path ending
}
else
{
// TODO: implement all cases in a generic way
continue;
}
// Add file
results.Add(String(fullPath));
}
}
closedir(dir);
return false;
}
bool MacFileSystem::getFilesFromDirectoryAll(Array<String>& results, const char* path, const char* searchPattern)
{
// Find all files in this directory
getFilesFromDirectoryTop(results, path, searchPattern);
size_t pathLength;
DIR* dir;
struct stat statPath, statEntry;
struct dirent* entry;
// Stat for the path
stat(path, &statPath);
// If path does not exists or is not dir - exit with status -1
if (S_ISDIR(statPath.st_mode) == 0)
{
// Is not directory
return true;
}
// If not possible to read the directory for this user
if ((dir = opendir(path)) == NULL)
{
// Cannot open directory
return true;
}
// The length of the path
pathLength = strlen(path);
// Iteration through entries in the directory
while ((entry = readdir(dir)) != NULL)
{
// Skip entries "." and ".."
if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, ".."))
continue;
// Determinate a full path of an entry
char full_path[256];
ASSERT(pathLength + strlen(entry->d_name) < ARRAY_COUNT(full_path));
strcpy(full_path, path);
strcat(full_path, "/");
strcat(full_path, entry->d_name);
// Stat for the entry
stat(full_path, &statEntry);
// Check for directory
if (S_ISDIR(statEntry.st_mode) != 0)
{
if (getFilesFromDirectoryAll(results, full_path, searchPattern))
{
closedir(dir);
return true;
}
}
}
closedir(dir);
return false;
}
DateTime MacFileSystem::GetFileLastEditTime(const StringView& path)
{
struct stat fileInfo;
const StringAsANSI<> pathANSI(*path, path.Length());
if (stat(pathANSI.Get(), &fileInfo) == -1)
{
return DateTime::MinValue();
}
const TimeSpan timeSinceEpoch(0, 0, fileInfo.st_mtime);
return UnixEpoch + timeSinceEpoch;
}
void MacFileSystem::GetSpecialFolderPath(const SpecialFolder type, String& result)
{
MISSING_CODE("MacFileSystem::GetSpecialFolderPath");
return; // TODO: filesystem on Mac
}
#endif

View File

@@ -0,0 +1,52 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_MAC
#include "Engine/Platform/Base/FileSystemBase.h"
/// <summary>
/// Mac platform implementation of filesystem service.
/// </summary>
class FLAXENGINE_API MacFileSystem : public FileSystemBase
{
public:
// [FileSystemBase]
static bool CreateDirectory(const StringView& path);
static bool DeleteDirectory(const String& path, bool deleteContents = true);
static bool DirectoryExists(const StringView& path);
static bool DirectoryGetFiles(Array<String, HeapAllocation>& results, const String& path, const Char* searchPattern, DirectorySearchOption option = DirectorySearchOption::AllDirectories);
static bool GetChildDirectories(Array<String, HeapAllocation>& results, const String& directory);
static bool FileExists(const StringView& path);
static bool DeleteFile(const StringView& path);
static uint64 GetFileSize(const StringView& path);
static bool IsReadOnly(const StringView& path);
static bool SetReadOnly(const StringView& path, bool isReadOnly);
static bool MoveFile(const StringView& dst, const StringView& src, bool overwrite = false);
static bool CopyFile(const StringView& dst, const StringView& src);
public:
/// <summary>
/// Gets last time when file has been modified (in UTC).
/// </summary>
/// <param name="path">The file path to check.</param>
/// <returns>The last write time or DateTime::MinValue() if cannot get data.</returns>
static DateTime GetFileLastEditTime(const StringView& path);
/// <summary>
/// Gets the special folder path.
/// </summary>
/// <param name="type">The folder type.</param>
/// <param name="result">The result full path.</param>
static void GetSpecialFolderPath(const SpecialFolder type, String& result);
private:
static bool getFilesFromDirectoryTop(Array<String, HeapAllocation>& results, const char* path, const char* searchPattern);
static bool getFilesFromDirectoryAll(Array<String, HeapAllocation>& results, const char* path, const char* searchPattern);
};
#endif

View File

@@ -0,0 +1,7 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#if PLATFORM_MAC
// TODO: networking on Mac
#endif

View File

@@ -0,0 +1,27 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_MAC
#include "Engine/Platform/Base/NetworkBase.h"
class FLAXENGINE_API MacNetwork : public NetworkBase
{
public:
// [NetworkBase]
static bool CreateSocket(NetworkSocket& socket, NetworkProtocol proto, NetworkIPVersion ipv);
static bool DestroySocket(NetworkSocket& socket);
static bool SetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32 value);
static bool GetSocketOption(NetworkSocket& socket, NetworkSocketOption option, int32& value);
static bool ConnectSocket(NetworkSocket& socket, NetworkEndPoint& endPoint);
static bool BindSocket(NetworkSocket& socket, NetworkEndPoint& endPoint);
static bool Listen(NetworkSocket& socket, uint16 queueSize);
static bool Accept(NetworkSocket& serverSocket, NetworkSocket& newSocket, NetworkEndPoint& newEndPoint);
static int32 WriteSocket(NetworkSocket socket, byte* data, uint32 length, NetworkEndPoint* endPoint = nullptr);
static int32 ReadSocket(NetworkSocket socket, byte* buffer, uint32 bufferSize, NetworkEndPoint* endPoint = nullptr);
static bool CreateEndPoint(const String& address, const String& port, NetworkIPVersion ipv, NetworkEndPoint& endPoint, bool bindable = true);
};
#endif

View File

@@ -0,0 +1,357 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#if PLATFORM_MAC
#include "MacPlatform.h"
#include "MacWindow.h"
#include "Engine/Core/Log.h"
#include "Engine/Core/Types/Guid.h"
#include "Engine/Core/Types/String.h"
#include "Engine/Core/Collections/HashFunctions.h"
#include "Engine/Core/Collections/Array.h"
#include "Engine/Core/Collections/Dictionary.h"
#include "Engine/Core/Collections/HashFunctions.h"
#include "Engine/Core/Math/Math.h"
#include "Engine/Core/Math/Rectangle.h"
#include "Engine/Core/Math/Color32.h"
#include "Engine/Platform/CPUInfo.h"
#include "Engine/Platform/MemoryStats.h"
#include "Engine/Platform/StringUtils.h"
#include "Engine/Platform/MessageBox.h"
#include "Engine/Platform/WindowsManager.h"
#include "Engine/Platform/Clipboard.h"
#include "Engine/Platform/IGuiData.h"
#include "Engine/Platform/Base/PlatformUtils.h"
#include "Engine/Utilities/StringConverter.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Engine/Engine.h"
#include "Engine/Engine/CommandLine.h"
#include "Engine/Input/Input.h"
#include "Engine/Input/Mouse.h"
#include "Engine/Input/Keyboard.h"
CPUInfo MacCpu;
Guid DeviceId;
String UserLocale, ComputerName;
byte MacAddress[6];
DialogResult MessageBox::Show(Window* parent, const StringView& text, const StringView& caption, MessageBoxButtons buttons, MessageBoxIcon icon)
{
if (CommandLine::Options.Headless)
return DialogResult::None;
todo;
}
class MacKeyboard : public Keyboard
{
public:
explicit MacKeyboard()
: Keyboard()
{
}
};
class MacMouse : public Mouse
{
public:
explicit MacMouse()
: Mouse()
{
}
public:
// [Mouse]
void SetMousePosition(const Vector2& newPosition) final override
{
MacPlatform::SetMousePosition(newPosition);
OnMouseMoved(newPosition);
}
};
bool MacPlatform::Is64BitPlatform()
{
return PLATFORM_64BITS;
}
CPUInfo MacPlatform::GetCPUInfo()
{
return MacCpu;
}
int32 MacPlatform::GetCacheLineSize()
{
return MacCpu.CacheLineSize;
}
MemoryStats MacPlatform::GetMemoryStats()
{
MISSING_CODE("MacPlatform::GetMemoryStats");
return MemoryStats(); // TODO: platform stats on Mac
}
ProcessMemoryStats MacPlatform::GetProcessMemoryStats()
{
MISSING_CODE("MacPlatform::GetProcessMemoryStats");
return ProcessMemoryStats(); // TODO: platform stats on Mac
}
uint64 MacPlatform::GetCurrentThreadID()
{
MISSING_CODE("MacPlatform::GetCurrentThreadID");
return 0; // TODO: threading on Mac
}
void MacPlatform::SetThreadPriority(ThreadPriority priority)
{
// TODO: impl this
}
void MacPlatform::SetThreadAffinityMask(uint64 affinityMask)
{
// TODO: impl this
}
void MacPlatform::Sleep(int32 milliseconds)
{
MISSING_CODE("MacPlatform::Sleep");
return; // TODO: clock on Mac
}
double MacPlatform::GetTimeSeconds()
{
MISSING_CODE("MacPlatform::GetTimeSeconds");
return 0.0; // TODO: clock on Mac
}
uint64 MacPlatform::GetTimeCycles()
{
MISSING_CODE("MacPlatform::GetTimeCycles");
return 0; // TODO: clock on Mac
}
uint64 MacPlatform::GetClockFrequency()
{
MISSING_CODE("MacPlatform::GetClockFrequency");
return 0; // TODO: clock on Mac
}
void MacPlatform::GetSystemTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond)
{
// Query for calendar time
struct timeval time;
gettimeofday(&time, nullptr);
// Convert to local time
struct tm localTime;
localtime_r(&time.tv_sec, &localTime);
// Extract time
year = localTime.tm_year + 1900;
month = localTime.tm_mon + 1;
dayOfWeek = localTime.tm_wday;
day = localTime.tm_mday;
hour = localTime.tm_hour;
minute = localTime.tm_min;
second = localTime.tm_sec;
millisecond = time.tv_usec / 1000;
}
void MacPlatform::GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond)
{
// Get the calendar time
struct timeval time;
gettimeofday(&time, nullptr);
// Convert to UTC time
struct tm localTime;
gmtime_r(&time.tv_sec, &localTime);
// Extract time
year = localTime.tm_year + 1900;
month = localTime.tm_mon + 1;
dayOfWeek = localTime.tm_wday;
day = localTime.tm_mday;
hour = localTime.tm_hour;
minute = localTime.tm_min;
second = localTime.tm_sec;
millisecond = time.tv_usec / 1000;
}
bool MacPlatform::Init()
{
if (PlatformBase::Init())
return true;
// TODO: get MacCpu
// TODO: get MacAddress
// TODO: get DeviceId
// TODO: get username
OnPlatformUserAdd(New<User>(TEXT("User")));
// TODO: get UserLocale
// TODO: get ComputerName
Input::Mouse = Impl::Mouse = New<MacMouse>();
Input::Keyboard = Impl::Keyboard = New<MacKeyboard>();
return false;
}
void MacPlatform::BeforeRun()
{
}
void MacPlatform::Tick()
{
// TODO: app events
}
void MacPlatform::BeforeExit()
{
}
void MacPlatform::Exit()
{
}
int32 MacPlatform::GetDpi()
{
return todo;
}
String MacPlatform::GetUserLocaleName()
{
return UserLocale;
}
String MacPlatform::GetComputerName()
{
return ComputerName;
}
bool MacPlatform::GetHasFocus()
{
// Check if any window is focused
ScopeLock lock(WindowsManager::WindowsLocker);
for (auto window : WindowsManager::Windows)
{
if (window->IsFocused())
return true;
}
// Default to true if has no windows open
return WindowsManager::Windows.IsEmpty();
}
bool MacPlatform::CanOpenUrl(const StringView& url)
{
return false;
}
void MacPlatform::OpenUrl(const StringView& url)
{
}
Vector2 MacPlatform::GetMousePosition()
{
MISSING_CODE("MacPlatform::GetMousePosition");
return Vector2(0, 0); // TODO: mouse on Mac
}
void MacPlatform::SetMousePosition(const Vector2& pos)
{
MISSING_CODE("MacPlatform::SetMousePosition");
// TODO: mouse on Mac
}
Vector2 MacPlatform::GetDesktopSize()
{
MISSING_CODE("MacPlatform::GetDesktopSize");
return Vector2(0, 0); // TODO: desktop size on Mac
}
Rectangle MacPlatform::GetMonitorBounds(const Vector2& screenPos)
{
// TODO: do it in a proper way
return Rectangle(Vector2::Zero, GetDesktopSize());
}
Rectangle MacPlatform::GetVirtualDesktopBounds()
{
// TODO: do it in a proper way
return Rectangle(Vector2::Zero, GetDesktopSize());
}
String MacPlatform::GetMainDirectory()
{
MISSING_CODE("MacPlatform::GetMainDirectory");
return TEXT("/"); // TODO: GetMainDirectory
}
String MacPlatform::GetExecutableFilePath()
{
MISSING_CODE("MacPlatform::GetExecutableFilePath");
return TEXT("/"); // TODO: GetMainDirectory
}
Guid MacPlatform::GetUniqueDeviceId()
{
return DeviceId;
}
String MacPlatform::GetWorkingDirectory()
{
char buffer[256];
getcwd(buffer, ARRAY_COUNT(buffer));
return String(buffer);
}
bool MacPlatform::SetWorkingDirectory(const String& path)
{
return chdir(StringAsANSI<>(*path).Get()) != 0;
}
Window* MacPlatform::CreateWindow(const CreateWindowSettings& settings)
{
return New<MacWindow>(settings);
}
bool MacPlatform::GetEnvironmentVariable(const String& name, String& value)
{
char* env = getenv(StringAsANSI<>(*name).Get());
if (env)
{
value = String(env);
return false;
}
return true;
}
bool MacPlatform::SetEnvironmentVariable(const String& name, const String& value)
{
return setenv(StringAsANSI<>(*name).Get(), StringAsANSI<>(*value).Get(), true) != 0;
}
void* MacPlatform::LoadLibrary(const Char* filename)
{
MISSING_CODE("MacPlatform::LoadLibrary");
return nullptr; // TODO: dynamic libs on Mac
}
void MacPlatform::FreeLibrary(void* handle)
{
MISSING_CODE("MacPlatform::FreeLibrary");
return; // TODO: dynamic libs on Mac
}
void* MacPlatform::GetProcAddress(void* handle, const char* symbol)
{
MISSING_CODE("MacPlatform::GetProcAddress");
return nullptr; // TODO: dynamic libs on Mac
}
#endif

View File

@@ -0,0 +1,112 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_MAC
#include "../Base/PlatformBase.h"
/// <summary>
/// The Mac platform implementation and application management utilities.
/// </summary>
class FLAXENGINE_API MacPlatform : public UnixPlatform
{
public:
// [UnixPlatform]
FORCE_INLINE static void MemoryBarrier()
{
__sync_synchronize();
}
FORCE_INLINE static int64 InterlockedExchange(int64 volatile* dst, int64 exchange)
{
return __sync_lock_test_and_set(dst, exchange);
}
FORCE_INLINE static int32 InterlockedCompareExchange(int32 volatile* dst, int32 exchange, int32 comperand)
{
return __sync_val_compare_and_swap(dst, comperand, exchange);
}
FORCE_INLINE static int64 InterlockedCompareExchange(int64 volatile* dst, int64 exchange, int64 comperand)
{
return __sync_val_compare_and_swap(dst, comperand, exchange);
}
FORCE_INLINE static int64 InterlockedIncrement(int64 volatile* dst)
{
return __sync_add_and_fetch(dst, 1);
}
FORCE_INLINE static int64 InterlockedDecrement(int64 volatile* dst)
{
return __sync_sub_and_fetch(dst, 1);
}
FORCE_INLINE static int64 InterlockedAdd(int64 volatile* dst, int64 value)
{
return __sync_fetch_and_add(dst, value);
}
FORCE_INLINE static int32 AtomicRead(int32 volatile* dst)
{
int32 result;
__atomic_load(dst, &result, __ATOMIC_SEQ_CST);
return result;
}
FORCE_INLINE static int64 AtomicRead(int64 volatile* dst)
{
int64 result;
__atomic_load(dst, &result, __ATOMIC_SEQ_CST);
return result;
}
FORCE_INLINE static void AtomicStore(int32 volatile* dst, int32 value)
{
__atomic_store(dst, &value, __ATOMIC_SEQ_CST);
}
FORCE_INLINE static void AtomicStore(int64 volatile* dst, int64 value)
{
__atomic_store(dst, &value, __ATOMIC_SEQ_CST);
}
FORCE_INLINE static void Prefetch(void const* ptr)
{
__builtin_prefetch(static_cast<char const*>(ptr));
}
static bool Is64BitPlatform();
static CPUInfo GetCPUInfo();
static int32 GetCacheLineSize();
static MemoryStats GetMemoryStats();
static ProcessMemoryStats GetProcessMemoryStats();
static uint64 GetCurrentThreadID();
static void SetThreadPriority(ThreadPriority priority);
static void SetThreadAffinityMask(uint64 affinityMask);
static void Sleep(int32 milliseconds);
static double GetTimeSeconds();
static uint64 GetTimeCycles();
static uint64 GetClockFrequency();
static void GetSystemTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond);
static void GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond);
static bool Init();
static void BeforeRun();
static void Tick();
static void BeforeExit();
static void Exit();
static int32 GetDpi();
static String GetUserLocaleName();
static String GetComputerName();
static bool GetHasFocus();
static bool CanOpenUrl(const StringView& url);
static void OpenUrl(const StringView& url);
static Vector2 GetMousePosition();
static void SetMousePosition(const Vector2& pos);
static Rectangle GetMonitorBounds(const Vector2& screenPos);
static Vector2 GetDesktopSize();
static Rectangle GetVirtualDesktopBounds();
static String GetMainDirectory();
static String GetExecutableFilePath();
static Guid GetUniqueDeviceId();
static String GetWorkingDirectory();
static bool SetWorkingDirectory(const String& path);
static Window* CreateWindow(const CreateWindowSettings& settings);
static bool GetEnvironmentVariable(const String& name, String& value);
static bool SetEnvironmentVariable(const String& name, const String& value);
static void* LoadLibrary(const Char* filename);
static void FreeLibrary(void* handle);
static void* GetProcAddress(void* handle, const char* symbol);
};
#endif

View File

@@ -0,0 +1,31 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_MAC || USE_EDITOR
#include "Engine/Core/Config/PlatformSettingsBase.h"
/// <summary>
/// Mac platform settings.
/// </summary>
API_CLASS(sealed, Namespace="FlaxEditor.Content.Settings") class FLAXENGINE_API MacPlatformSettings : public SettingsBase
{
DECLARE_SCRIPTING_TYPE_MINIMAL(MacPlatformSettings);
/// <summary>
/// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use.
/// </summary>
static MacPlatformSettings* Get();
// [SettingsBase]
void Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) final override
{
}
};
#if PLATFORM_MAC
typedef MacPlatformSettings PlatformSettings;
#endif
#endif

View File

@@ -0,0 +1,277 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#if PLATFORM_MAC
#include "Engine/Platform/StringUtils.h"
#include <wctype.h>
#include <cctype>
#include <wchar.h>
#include <cstring>
#include <stdlib.h>
bool StringUtils::IsUpper(char c)
{
return isupper(c) != 0;
}
bool StringUtils::IsLower(char c)
{
return islower(c) != 0;
}
bool StringUtils::IsAlpha(char c)
{
return iswalpha(c) != 0;
}
bool StringUtils::IsPunct(char c)
{
return ispunct(c) != 0;
}
bool StringUtils::IsAlnum(char c)
{
return isalnum(c) != 0;
}
bool StringUtils::IsDigit(char c)
{
return isdigit(c) != 0;
}
bool StringUtils::IsHexDigit(char c)
{
return isxdigit(c) != 0;
}
bool StringUtils::IsWhitespace(char c)
{
return isspace(c) != 0;
}
char StringUtils::ToUpper(char c)
{
return toupper(c);
}
char StringUtils::ToLower(char c)
{
return tolower(c);
}
bool StringUtils::IsUpper(Char c)
{
return iswupper(c) != 0;
}
bool StringUtils::IsLower(Char c)
{
return iswlower(c) != 0;
}
bool StringUtils::IsAlpha(Char c)
{
return iswalpha(c) != 0;
}
bool StringUtils::IsPunct(Char c)
{
return iswpunct(c) != 0;
}
bool StringUtils::IsAlnum(Char c)
{
return iswalnum(c) != 0;
}
bool StringUtils::IsDigit(Char c)
{
return iswdigit(c) != 0;
}
bool StringUtils::IsHexDigit(Char c)
{
return iswxdigit(c) != 0;
}
bool StringUtils::IsWhitespace(Char c)
{
return iswspace(c) != 0;
}
Char StringUtils::ToUpper(Char c)
{
return towupper(c);
}
Char StringUtils::ToLower(Char c)
{
return towlower(c);
}
int32 StringUtils::Compare(const Char* str1, const Char* str2)
{
Char c1, c2;
int32 i;
do
{
c1 = *str1++;
c2 = *str2++;
i = (int32)c1 - (int32)c2;
} while (i == 0 && c1 && c2);
return i;
}
int32 StringUtils::Compare(const Char* str1, const Char* str2, int32 maxCount)
{
Char c1, c2;
int32 i;
if (maxCount == 0)
return 0;
do
{
c1 = *str1++;
c2 = *str2++;
i = (int32)c1 - (int32)c2;
maxCount--;
} while (i == 0 && c1 && c2 && maxCount);
return i;
}
int32 StringUtils::CompareIgnoreCase(const Char* str1, const Char* str2)
{
Char c1, c2;
int32 i;
do
{
c1 = ToLower(*str1++);
c2 = ToLower(*str2++);
i = (int32)c1 - (int32)c2;
} while (i == 0 && c1 && c2);
return i;
}
int32 StringUtils::CompareIgnoreCase(const Char* str1, const Char* str2, int32 maxCount)
{
Char c1, c2;
int32 i;
if (maxCount == 0)
return 0;
do
{
c1 = ToLower(*str1++);
c2 = ToLower(*str2++);
i = (int32)c1 - (int32)c2;
maxCount--;
} while (i == 0 && c1 && c2 && maxCount);
return i;
}
int32 StringUtils::Length(const Char* str)
{
if (!str)
return 0;
const Char* ptr = str;
for (; *ptr; ++ptr)
{
}
return ptr - str;
}
int32 StringUtils::Length(const char* str)
{
if (!str)
return 0;
return static_cast<int32>(strlen(str));
}
int32 StringUtils::Compare(const char* str1, const char* str2)
{
return strcmp(str1, str2);
}
int32 StringUtils::Compare(const char* str1, const char* str2, int32 maxCount)
{
return strncmp(str1, str2, maxCount);
}
int32 StringUtils::CompareIgnoreCase(const char* str1, const char* str2)
{
return strcasecmp(str1, str2);
}
int32 StringUtils::CompareIgnoreCase(const char* str1, const char* str2, int32 maxCount)
{
return strncasecmp(str1, str2, maxCount);
}
Char* StringUtils::Copy(Char* dst, const Char* src)
{
Char* q = dst;
const Char* p = src;
Char ch;
do
{
*q++ = ch = *p++;
} while (ch);
return dst;
}
Char* StringUtils::Copy(Char* dst, const Char* src, int32 count)
{
Char* q = dst;
const Char* p = src;
char ch;
while (count)
{
count--;
*q++ = ch = *p++;
if (!ch)
break;
}
*q = 0;
return dst;
}
const Char* StringUtils::Find(const Char* str, const Char* toFind)
{
while (*str)
{
const Char* start = str;
const Char* sub = toFind;
// If first character of sub string match, check for whole string
while (*str && *sub && *str == *sub)
{
str++;
sub++;
}
// If complete substring match, return starting address
if (!*sub)
return (Char*)start;
// Increment main string
str = start + 1;
}
// No matches
return nullptr;
}
const char* StringUtils::Find(const char* str, const char* toFind)
{
return strstr(str, toFind);
}
void StringUtils::ConvertANSI2UTF16(const char* from, Char* to, int32 len)
{
todo;
}
void StringUtils::ConvertUTF162ANSI(const Char* from, char* to, int32 len)
{
todo;
}
#endif

View File

@@ -0,0 +1,23 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#if PLATFORM_MAC
#include "MacThread.h"
#include "Engine/Threading/IRunnable.h"
#include "Engine/Threading/ThreadRegistry.h"
MacThread::MacThread(IRunnable* runnable, const String& name, ThreadPriority priority)
: ThreadBase(runnable, name, priority)
{
}
MacThread::~MacThread()
{
}
MacThread* Create(IRunnable* runnable, const String& name, ThreadPriority priority = ThreadPriority::Normal, uint32 stackSize = 0)
{
// TODO: imp this
}
#endif

View File

@@ -0,0 +1,31 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_MAC
#include "../Base/ThreadBase.h"
/// <summary>
/// Thread object for Mac platform.
/// </summary>
class MacThread : public ThreadBase
{
public:
MacThread(IRunnable* runnable, const String& name, ThreadPriority priority);
~MacThread();
static MacThread* Create(IRunnable* runnable, const String& name, ThreadPriority priority = ThreadPriority::Normal, uint32 stackSize = 0);
// [ThreadBase]
void Join() override;
protected:
// [ThreadBase]
void ClearHandleInternal();
void SetPriorityInternal(ThreadPriority priority);
void KillInternal(bool waitForJoin);
};
#endif

View File

@@ -0,0 +1,103 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#if PLATFORM_MAC
#include "../Window.h"
MacWindow::MacWindow(const CreateWindowSettings& settings)
: WindowBase(settings)
{
int32 x = Math::TruncToInt(settings.Position.X);
int32 y = Math::TruncToInt(settings.Position.Y);
int32 clientWidth = Math::TruncToInt(settings.Size.X);
int32 clientHeight = Math::TruncToInt(settings.Size.Y);
int32 windowWidth = clientWidth;
int32 windowHeight = clientHeight;
_clientSize = Vector2((float)clientWidth, (float)clientHeight);
// TODO: setup window
}
MacWindow::~MacWindow()
{
// TODO: close window
}
void* MacWindow::GetNativePtr() const
{
// TODO: return window handle
return nullptr;
}
void MacWindow::Show()
{
if (!_visible)
{
InitSwapChain();
if (_showAfterFirstPaint)
{
if (RenderTask)
RenderTask->Enabled = true;
return;
}
// Show
// TODO: show window
_focused = true;
// Base
WindowBase::Show();
}
}
void MacWindow::Hide()
{
if (_visible)
{
// Hide
// TODO: hide window
// Base
WindowBase::Hide();
}
}
void MacWindow::Minimize()
{
// TODO: Minimize
}
void MacWindow::Maximize()
{
// TODO: Maximize
}
void MacWindow::Restore()
{
// TODO: Restore
}
bool MacWindow::IsClosed() const
{
return false;
}
bool MacWindow::IsForegroundWindow() const
{
return Platform::GetHasFocus();
}
void MacWindow::SetIsFullscreen(bool isFullscreen)
{
}
void MacWindow::GetScreenInfo(int32& x, int32& y, int32& width, int32& height) const
{
// TODO: proper screen info
x = 0;
y = 0;
width = (int32)_clientSize.X;
height = (int32)_clientSize.Y;
}
#endif

View File

@@ -0,0 +1,38 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#pragma once
#if PLATFORM_MAC
#include "Engine/Platform/Base/WindowBase.h"
#include "Engine/Platform/Platform.h"
/// <summary>
/// Implementation of the window class for Mac platform.
/// </summary>
class MacWindow : public WindowBase
{
private:
Vector2 _clientSize;
public:
MacWindow(const CreateWindowSettings& settings);
~MacWindow();
public:
// [Window]
void* GetNativePtr() const override;
void Show() override;
void Hide() override;
void Minimize() override;
void Maximize() override;
void Restore() override;
bool IsClosed() const override;
bool IsForegroundWindow() const override;
void SetIsFullscreen(bool isFullscreen) override;
};
#endif

View File

@@ -2,24 +2,18 @@
#pragma once #pragma once
#if PLATFORM_WINDOWS #if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT
#include "Win32/Win32Network.h" #include "Win32/Win32Network.h"
#elif PLATFORM_UWP #elif PLATFORM_LINUX || PLATFORM_ANDROID
#include "Win32/Win32Network.h"
#elif PLATFORM_LINUX
#include "Unix/UnixNetwork.h" #include "Unix/UnixNetwork.h"
#elif PLATFORM_PS4 #elif PLATFORM_PS4
#include "Platforms/PS4/Engine/Platform/PS4Network.h" #include "Platforms/PS4/Engine/Platform/PS4Network.h"
#elif PLATFORM_PS5 #elif PLATFORM_PS5
#include "Platforms/PS5/Engine/Platform/PS5Network.h" #include "Platforms/PS5/Engine/Platform/PS5Network.h"
#elif PLATFORM_XBOX_ONE
#include "Win32/Win32Network.h"
#elif PLATFORM_XBOX_SCARLETT
#include "Win32/Win32Network.h"
#elif PLATFORM_ANDROID
#include "Unix/UnixNetwork.h"
#elif PLATFORM_SWITCH #elif PLATFORM_SWITCH
#include "Platforms/Switch/Engine/Platform/SwitchNetwork.h" #include "Platforms/Switch/Engine/Platform/SwitchNetwork.h"
#elif PLATFORM_MAC
#include "Mac/MacNetwork.h"
#else #else
#error Missing Network implementation! #error Missing Network implementation!
#endif #endif

View File

@@ -77,6 +77,9 @@ public class Platform : EngineModule
case TargetPlatform.Switch: case TargetPlatform.Switch:
options.SourcePaths.Add(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Switch", "Engine", "Platform")); options.SourcePaths.Add(Path.Combine(Globals.EngineRoot, "Source", "Platforms", "Switch", "Engine", "Platform"));
break; break;
case TargetPlatform.Mac:
options.SourcePaths.Add(Path.Combine(FolderPath, "Mac"));
break;
default: throw new InvalidPlatformException(options.Platform.Target); default: throw new InvalidPlatformException(options.Platform.Target);
} }
if (options.Target.IsEditor) if (options.Target.IsEditor)
@@ -87,6 +90,7 @@ public class Platform : EngineModule
options.SourceFiles.Add(Path.Combine(FolderPath, "Linux", "LinuxPlatformSettings.h")); options.SourceFiles.Add(Path.Combine(FolderPath, "Linux", "LinuxPlatformSettings.h"));
options.SourceFiles.Add(Path.Combine(FolderPath, "Android", "AndroidPlatformSettings.h")); options.SourceFiles.Add(Path.Combine(FolderPath, "Android", "AndroidPlatformSettings.h"));
options.SourceFiles.Add(Path.Combine(FolderPath, "GDK", "GDKPlatformSettings.h")); options.SourceFiles.Add(Path.Combine(FolderPath, "GDK", "GDKPlatformSettings.h"));
options.SourceFiles.Add(Path.Combine(FolderPath, "Mac", "MacPlatformSettings.h"));
AddSourceFileIfExists(options, Path.Combine(Globals.EngineRoot, "Source", "Platforms", "XboxOne", "Engine", "Platform", "XboxOnePlatformSettings.h")); AddSourceFileIfExists(options, Path.Combine(Globals.EngineRoot, "Source", "Platforms", "XboxOne", "Engine", "Platform", "XboxOnePlatformSettings.h"));
AddSourceFileIfExists(options, Path.Combine(Globals.EngineRoot, "Source", "Platforms", "XboxScarlett", "Engine", "Platform", "XboxScarlettPlatformSettings.h")); AddSourceFileIfExists(options, Path.Combine(Globals.EngineRoot, "Source", "Platforms", "XboxScarlett", "Engine", "Platform", "XboxScarlettPlatformSettings.h"));
AddSourceFileIfExists(options, Path.Combine(Globals.EngineRoot, "Source", "Platforms", "PS4", "Engine", "Platform", "PS4PlatformSettings.h")); AddSourceFileIfExists(options, Path.Combine(Globals.EngineRoot, "Source", "Platforms", "PS4", "Engine", "Platform", "PS4PlatformSettings.h"));

View File

@@ -26,6 +26,8 @@
#include "Android/AndroidPlatform.h" #include "Android/AndroidPlatform.h"
#elif PLATFORM_SWITCH #elif PLATFORM_SWITCH
#include "Platforms/Switch/Engine/Platform/SwitchPlatform.h" #include "Platforms/Switch/Engine/Platform/SwitchPlatform.h"
#elif PLATFORM_MAC
#include "Mac/MacPlatform.h"
#else #else
#error Missing Platform implementation! #error Missing Platform implementation!
#endif #endif

View File

@@ -2,9 +2,7 @@
#pragma once #pragma once
#if PLATFORM_WINDOWS #if PLATFORM_WINDOWS || PLATFORM_UWP || PLATFORM_XBOX_ONE || PLATFORM_XBOX_SCARLETT
#include "Win32/Win32Thread.h"
#elif PLATFORM_UWP
#include "Win32/Win32Thread.h" #include "Win32/Win32Thread.h"
#elif PLATFORM_LINUX #elif PLATFORM_LINUX
#include "Linux/LinuxThread.h" #include "Linux/LinuxThread.h"
@@ -12,14 +10,12 @@
#include "Platforms/PS4/Engine/Platform/PS4Thread.h" #include "Platforms/PS4/Engine/Platform/PS4Thread.h"
#elif PLATFORM_PS5 #elif PLATFORM_PS5
#include "Platforms/PS5/Engine/Platform/PS5Thread.h" #include "Platforms/PS5/Engine/Platform/PS5Thread.h"
#elif PLATFORM_XBOX_ONE
#include "Win32/Win32Thread.h"
#elif PLATFORM_XBOX_SCARLETT
#include "Win32/Win32Thread.h"
#elif PLATFORM_ANDROID #elif PLATFORM_ANDROID
#include "Android/AndroidThread.h" #include "Android/AndroidThread.h"
#elif PLATFORM_SWITCH #elif PLATFORM_SWITCH
#include "Platforms/Switch/Engine/Platform/SwitchThread.h" #include "Platforms/Switch/Engine/Platform/SwitchThread.h"
#elif PLATFORM_MAC
#include "Mac/MacThread.h"
#else #else
#error Missing Thread implementation! #error Missing Thread implementation!
#endif #endif

View File

@@ -147,8 +147,8 @@ class Win32Thread;
typedef Win32Thread Thread; typedef Win32Thread Thread;
class GDKWindow; class GDKWindow;
typedef GDKWindow Window; typedef GDKWindow Window;
class NetworkBase; class Win32Network;
typedef NetworkBase Network; typedef Win32Network Network;
class GDKUser; class GDKUser;
typedef GDKUser User; typedef GDKUser User;
@@ -172,8 +172,8 @@ class Win32Thread;
typedef Win32Thread Thread; typedef Win32Thread Thread;
class GDKWindow; class GDKWindow;
typedef GDKWindow Window; typedef GDKWindow Window;
class NetworkBase; class Win32Network;
typedef NetworkBase Network; typedef Win32Network Network;
class GDKUser; class GDKUser;
typedef GDKUser User; typedef GDKUser User;
@@ -227,6 +227,31 @@ typedef SwitchNetwork Network;
class UserBase; class UserBase;
typedef UserBase User; typedef UserBase User;
#elif PLATFORM_MAC
class ClipboardBase;
typedef ClipboardBase Clipboard;
class MacCriticalSection;
typedef MacCriticalSection CriticalSection;
class MacConditionVariable;
typedef MacConditionVariable ConditionVariable;
class MacFileSystem;
typedef MacFileSystem FileSystem;
class FileSystemWatcherBase;
typedef FileSystemWatcherBase FileSystemWatcher;
class MacFile;
typedef MacFile File;
class MacPlatform;
typedef MacPlatform Platform;
class MacThread;
typedef MacThread Thread;
class MacWindow;
typedef MacWindow Window;
class MacNetwork;
typedef MacNetwork Network;
class UserBase;
typedef UserBase User;
#else #else
#error Missing Types implementation! #error Missing Types implementation!

View File

@@ -20,6 +20,8 @@
#include "Platforms/Switch/Engine/Platform/SwitchWindow.h" #include "Platforms/Switch/Engine/Platform/SwitchWindow.h"
#elif PLATFORM_PS5 #elif PLATFORM_PS5
#include "Platforms/PS5/Engine/Platform/PS5Window.h" #include "Platforms/PS5/Engine/Platform/PS5Window.h"
#elif PLATFORM_MAC
#include "Mac/MacWindow.h"
#else #else
#error Missing Window implementation! #error Missing Window implementation!
#endif #endif

View File

@@ -35,6 +35,7 @@ public class TextureTool : EngineModule
case TargetPlatform.PS5: case TargetPlatform.PS5:
case TargetPlatform.Android: case TargetPlatform.Android:
case TargetPlatform.Switch: case TargetPlatform.Switch:
case TargetPlatform.Mac:
useStb = true; useStb = true;
break; break;
default: throw new InvalidPlatformException(options.Platform.Target); default: throw new InvalidPlatformException(options.Platform.Target);

View File

@@ -769,6 +769,7 @@ void ShaderGenerator::ProcessGroupTools(Box* box, Node* node, Value& value)
PLATFORM_CASE(8, "PLATFORM_ANDROID"); PLATFORM_CASE(8, "PLATFORM_ANDROID");
PLATFORM_CASE(9, "PLATFORM_SWITCH"); PLATFORM_CASE(9, "PLATFORM_SWITCH");
PLATFORM_CASE(10, "PLATFORM_PS5"); PLATFORM_CASE(10, "PLATFORM_PS5");
PLATFORM_CASE(11, "PLATFORM_MAC");
#undef PLATFORM_CASE #undef PLATFORM_CASE
break; break;
} }

View File

@@ -917,6 +917,9 @@ void VisjectExecutor::ProcessGroupTools(Box* box, Node* node, Value& value)
case PlatformType::PS5: case PlatformType::PS5:
boxId = 10; boxId = 10;
break; break;
case PlatformType::Mac:
boxId = 11;
break;
default: ; default: ;
} }
value = tryGetValue(node->GetBox(node->GetBox(boxId)->HasConnection() ? boxId : 1), Value::Zero); value = tryGetValue(node->GetBox(node->GetBox(boxId)->HasConnection() ? boxId : 1), Value::Zero);

View File

@@ -97,6 +97,7 @@ namespace Flax.Build
case TargetPlatform.XboxScarlett: return "PLATFORM_XBOX_SCARLETT"; case TargetPlatform.XboxScarlett: return "PLATFORM_XBOX_SCARLETT";
case TargetPlatform.Android: return "PLATFORM_ANDROID"; case TargetPlatform.Android: return "PLATFORM_ANDROID";
case TargetPlatform.Switch: return "PLATFORM_SWITCH"; case TargetPlatform.Switch: return "PLATFORM_SWITCH";
case TargetPlatform.Mac: return "PLATFORM_MAC";
default: throw new InvalidPlatformException(platform); default: throw new InvalidPlatformException(platform);
} }
} }

View File

@@ -34,6 +34,7 @@ namespace Flax.Build
case PlatformID.Win32Windows: case PlatformID.Win32Windows:
case PlatformID.WinCE: return TargetPlatform.Windows; case PlatformID.WinCE: return TargetPlatform.Windows;
case PlatformID.Unix: return TargetPlatform.Linux; case PlatformID.Unix: return TargetPlatform.Linux;
case PlatformID.MacOSX: return TargetPlatform.Mac;
default: throw new NotImplementedException(string.Format("Unsupported build platform {0}.", platformId)); default: throw new NotImplementedException(string.Format("Unsupported build platform {0}.", platformId));
} }
} }
@@ -242,6 +243,7 @@ namespace Flax.Build
case TargetPlatform.PS5: return targetArchitecture == TargetArchitecture.x64; case TargetPlatform.PS5: return targetArchitecture == TargetArchitecture.x64;
case TargetPlatform.Android: return targetArchitecture == TargetArchitecture.ARM64; case TargetPlatform.Android: return targetArchitecture == TargetArchitecture.ARM64;
case TargetPlatform.Switch: return targetArchitecture == TargetArchitecture.ARM64; case TargetPlatform.Switch: return targetArchitecture == TargetArchitecture.ARM64;
case TargetPlatform.Mac: return targetArchitecture == TargetArchitecture.x64;
default: return false; default: return false;
} }
} }

View File

@@ -51,6 +51,11 @@ namespace Flax.Build
/// Running on PlayStation 5. /// Running on PlayStation 5.
/// </summary> /// </summary>
PS5 = 9, PS5 = 9,
/// <summary>
/// Running on Mac.
/// </summary>
Mac = 10,
} }
/// <summary> /// <summary>

View File

@@ -239,6 +239,7 @@ namespace Flax.Deps
case TargetPlatform.Linux: case TargetPlatform.Linux:
case TargetPlatform.PS4: case TargetPlatform.PS4:
case TargetPlatform.PS5: case TargetPlatform.PS5:
case TargetPlatform.Mac:
{ {
cmdLine = "CMakeLists.txt"; cmdLine = "CMakeLists.txt";
break; break;

View File

@@ -139,6 +139,7 @@
<Compile Include="Platforms\Linux\*.cs" /> <Compile Include="Platforms\Linux\*.cs" />
<Compile Include="..\..\Platforms\PS4\Flax.Build\*.cs" /> <Compile Include="..\..\Platforms\PS4\Flax.Build\*.cs" />
<Compile Include="..\..\Platforms\PS5\Flax.Build\*.cs" /> <Compile Include="..\..\Platforms\PS5\Flax.Build\*.cs" />
<Compile Include="Platforms\Mac\*.cs" />
<Compile Include="Platforms\Unix\*.cs" /> <Compile Include="Platforms\Unix\*.cs" />
<Compile Include="Platforms\UWP\*.cs" /> <Compile Include="Platforms\UWP\*.cs" />
<Compile Include="Platforms\Windows\*.cs" /> <Compile Include="Platforms\Windows\*.cs" />
@@ -160,6 +161,7 @@
<Compile Include="Projects\VisualStudio\VisualStudioProject.cs" /> <Compile Include="Projects\VisualStudio\VisualStudioProject.cs" />
<Compile Include="Projects\VisualStudio\VisualStudioProjectGenerator.cs" /> <Compile Include="Projects\VisualStudio\VisualStudioProjectGenerator.cs" />
<Compile Include="Projects\VisualStudio\VisualStudioVersion.cs" /> <Compile Include="Projects\VisualStudio\VisualStudioVersion.cs" />
<Compile Include="Projects\XCode.cs" />
<Compile Include="Utilities\StringWriterWithEncoding.cs" /> <Compile Include="Utilities\StringWriterWithEncoding.cs" />
<Compile Include="Utilities\Tokenizer.cs" /> <Compile Include="Utilities\Tokenizer.cs" />
<Compile Include="Utilities\TwoWayEnumerator.cs" /> <Compile Include="Utilities\TwoWayEnumerator.cs" />

View File

@@ -36,6 +36,7 @@ namespace Flax.Build
TargetPlatform.XboxScarlett, TargetPlatform.XboxScarlett,
TargetPlatform.Android, TargetPlatform.Android,
TargetPlatform.Switch, TargetPlatform.Switch,
TargetPlatform.Mac,
}; };
/// <summary> /// <summary>

View File

@@ -0,0 +1,60 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using Flax.Build.Projects;
namespace Flax.Build.Platforms
{
/// <summary>
/// The build platform for all Mac systems.
/// </summary>
/// <seealso cref="Platform" />
public sealed class MacPlatform : Platform
{
/// <inheritdoc />
public override TargetPlatform Target => TargetPlatform.Mac;
/// <inheritdoc />
public override bool HasRequiredSDKsInstalled { get; }
/// <inheritdoc />
public override bool HasSharedLibrarySupport => true;
/// <inheritdoc />
public override string ExecutableFileExtension => string.Empty;
/// <inheritdoc />
public override string SharedLibraryFileExtension => ".dylib";
/// <inheritdoc />
public override string StaticLibraryFileExtension => ".a";
/// <inheritdoc />
public override string ProgramDatabaseFileExtension => ".dSYM";
/// <inheritdoc />
public override string SharedLibraryFilePrefix => string.Empty;
/// <inheritdoc />
public override string StaticLibraryFilePrefix => "lib";
/// <inheritdoc />
public override ProjectFormat DefaultProjectFormat => ProjectFormat.XCode;
/// <summary>
/// Initializes a new instance of the <see cref="Flax.Build.Platforms.MacPlatform"/> class.
/// </summary>
public MacPlatform()
{
if (Platform.BuildTargetPlatform != TargetPlatform.Mac)
return;
throw new System.NotImplementedException("TODO: detect MacSDK installation");
}
/// <inheritdoc />
protected override Toolchain CreateToolchain(TargetArchitecture architecture)
{
return new MacToolchain(this, architecture);
}
}
}

View File

@@ -0,0 +1,58 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using Flax.Build.Graph;
using Flax.Build.NativeCpp;
namespace Flax.Build.Platforms
{
/// <summary>
/// The build toolchain for all Mac systems.
/// </summary>
/// <seealso cref="Toolchain" />
public sealed class MacToolchain : Toolchain
{
/// <summary>
/// Initializes a new instance of the <see cref="MacToolchain"/> class.
/// </summary>
/// <param name="platform">The platform.</param>
/// <param name="architecture">The target architecture.</param>
public MacToolchain(MacPlatform platform, TargetArchitecture architecture)
: base(platform, architecture)
{
}
/// <inheritdoc />
public override string DllExport => "__attribute__((__visibility__(\\\"default\\\")))";
/// <inheritdoc />
public override string DllImport => "";
/// <inheritdoc />
public override void LogInfo()
{
throw new NotImplementedException("TODO: MacToolchain.LogInfo");
}
/// <inheritdoc />
public override void SetupEnvironment(BuildOptions options)
{
base.SetupEnvironment(options);
options.CompileEnv.PreprocessorDefinitions.Add("PLATFORM_MAC");
}
/// <inheritdoc />
public override CompileOutput CompileCppFiles(TaskGraph graph, BuildOptions options, List<string> sourceFiles, string outputPath)
{
throw new NotImplementedException("TODO: MacToolchain.CompileCppFiles");
}
/// <inheritdoc />
public override void LinkFiles(TaskGraph graph, BuildOptions options, string outputFilePath)
{
throw new NotImplementedException("TODO: MacToolchain.LinkFiles");
}
}
}

View File

@@ -41,5 +41,10 @@ namespace Flax.Build.Projects
/// Visual Studio Code. /// Visual Studio Code.
/// </summary> /// </summary>
VisualStudioCode, VisualStudioCode,
/// <summary>
/// XCode.
/// </summary>
XCode,
} }
} }

View File

@@ -139,6 +139,7 @@ namespace Flax.Build.Projects
} }
} }
case ProjectFormat.VisualStudioCode: return new VisualStudioCodeProjectGenerator(); case ProjectFormat.VisualStudioCode: return new VisualStudioCodeProjectGenerator();
case ProjectFormat.XCode: return new XCodeProjectGenerator();
case ProjectFormat.Custom: case ProjectFormat.Custom:
if (CustomProjectTypes.TryGetValue(Configuration.ProjectFormatCustom, out var factory)) if (CustomProjectTypes.TryGetValue(Configuration.ProjectFormatCustom, out var factory))
return factory(type); return factory(type);

View File

@@ -0,0 +1,49 @@
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
using System;
namespace Flax.Build.Projects
{
/// <summary>
/// Project generator for XCode.
/// </summary>
public class XCodeProjectGenerator : ProjectGenerator
{
/// <summary>
/// Initializes a new instance of the <see cref="XCodeProjectGenerator"/> class.
/// </summary>
public XCodeProjectGenerator()
{
}
/// <inheritdoc />
public override string ProjectFileExtension => "xcodeproj";
/// <inheritdoc />
public override string SolutionFileExtension => string.Empty;
/// <inheritdoc />
public override TargetType? Type => null;
/// <inheritdoc />
public override Project CreateProject()
{
return new Project
{
Generator = this,
};
}
/// <inheritdoc />
public override void GenerateProject(Project project)
{
throw new NotImplementedException("TODO: XCode");
}
/// <inheritdoc />
public override void GenerateSolution(Solution solution)
{
throw new NotImplementedException("TODO: XCode");
}
}
}