浏览代码

Added Android support, and generalized target triple support

Added PICLevel, RelocKind
DarwinCommon/LinuxCommon/AndroidCommon merged into PosixCommon
Mangling changed to avoid '@'
Brian Fiete 5 年之前
父节点
当前提交
3883a3674d
共有 39 个文件被更改,包括 3450 次插入5628 次删除
  1. 1 1
      BeefBoot/BeefBoot.cpp
  2. 3 3
      BeefBoot/BootApp.cpp
  3. 2 2
      BeefBuild/BeefProj.toml
  4. 78 4
      BeefRT/CMakeLists.txt
  5. 2 2
      BeefRT/rt/Internal.cpp
  6. 8 0
      BeefySysLib/platform/android/AndroidCommon.cpp
  7. 159 0
      BeefySysLib/platform/android/AndroidCommon.h
  8. 13 0
      BeefySysLib/platform/android/BFPlatform.cpp
  9. 24 0
      BeefySysLib/platform/android/BFPlatform.h
  10. 9 0
      BeefySysLib/platform/android/PlatformApp.h
  11. 4 3291
      BeefySysLib/platform/darwin/DarwinCommon.cpp
  12. 1 0
      BeefySysLib/platform/ios/BFPlatform.h
  13. 5 2207
      BeefySysLib/platform/linux/LinuxCommon.cpp
  14. 2 0
      BeefySysLib/platform/osx/BFPlatform.h
  15. 2341 0
      BeefySysLib/platform/posix/PosixCommon.cpp
  16. 3 5
      BeefySysLib/util/BeefPerf.cpp
  17. 8 0
      BeefySysLib/util/Hash.cpp
  18. 4 0
      BeefySysLib/util/Hash.h
  19. 88 1
      IDE/src/BuildContext.bf
  20. 19 0
      IDE/src/BuildOptions.bf
  21. 5 1
      IDE/src/Compiler/BfCompiler.bf
  22. 4 3
      IDE/src/Compiler/BfProject.bf
  23. 92 19
      IDE/src/IDEApp.bf
  24. 65 12
      IDE/src/Project.bf
  25. 20 5
      IDE/src/Workspace.bf
  26. 3 1
      IDE/src/ui/ProjectProperties.bf
  27. 7 4
      IDE/src/ui/WorkspaceProperties.bf
  28. 55 0
      IDE/src/util/TargetTriple.bf
  29. 62 15
      IDEHelper/Compiler/BfCompiler.cpp
  30. 5 1
      IDEHelper/Compiler/BfCompiler.h
  31. 26 5
      IDEHelper/Compiler/BfContext.cpp
  32. 2 2
      IDEHelper/Compiler/BfIRBuilder.cpp
  33. 41 1
      IDEHelper/Compiler/BfIRCodeGen.cpp
  34. 28 32
      IDEHelper/Compiler/BfMangler.cpp
  35. 5 5
      IDEHelper/Compiler/BfModule.cpp
  36. 4 2
      IDEHelper/Compiler/BfSystem.cpp
  37. 40 2
      IDEHelper/Compiler/BfSystem.h
  38. 2 2
      IDEHelper/IDEHelper.vcxproj
  39. 210 0
      bin/build_android.bat

+ 1 - 1
BeefBoot/BeefBoot.cpp

@@ -33,7 +33,7 @@ BF_IMPORT void BF_CALLTYPE IDEHelper_ProgramStart();
 BF_IMPORT void BF_CALLTYPE IDEHelper_ProgramDone();
 
 int main(int argc, char* argv[])
-{		
+{	
 #ifdef TEST_CRASH
 	CrashCatcher catcher;
 	catcher.SetCrashReportKind(BfpCrashReportKind_GUI);

+ 3 - 3
BeefBoot/BootApp.cpp

@@ -57,7 +57,7 @@ BF_IMPORT void BF_CALLTYPE BfSystem_AddTypeOptions(void* bfSystem, const char* f
 
 BF_IMPORT void BF_CALLTYPE BfProject_SetDisabled(void* bfProject, bool disabled);
 BF_IMPORT void BF_CALLTYPE BfProject_SetOptions(void* bfProject, int targetType, const char* startupObject, const char* preprocessorMacros,
-	int optLevel, int ltoType, int32 flags);
+	int optLevel, int ltoType, int relocType, int picLevel, int32 flags);
 BF_IMPORT void BF_CALLTYPE BfProject_ClearDependencies(void* bfProject);
 BF_IMPORT void BF_CALLTYPE BfProject_AddDependency(void* bfProject, void* depProject);
 
@@ -795,7 +795,7 @@ bool BootApp::Compile()
 		mCELibProject = BfSystem_CreateProject(mSystem, "BeefLib");		
 
 		BfProjectFlags flags = BfProjectFlags_None;
-		BfProject_SetOptions(mCELibProject, BfTargetType_BeefLib, "", mDefines.c_str(), mOptLevel, 0, flags);		
+		BfProject_SetOptions(mCELibProject, BfTargetType_BeefLib, "", mDefines.c_str(), mOptLevel, 0, 0, 0, flags);		
 	}
 
 	if (!mDefines.IsEmpty())
@@ -815,7 +815,7 @@ bool BootApp::Compile()
 		else if (mAsmKind == BfAsmKind_Intel)
 			flags = (BfProjectFlags)(flags | BfProjectFlags_AsmOutput);
 	}
-    BfProject_SetOptions(mProject, mTargetType, mStartupObject.c_str(), mDefines.c_str(), mOptLevel, ltoType, flags);
+    BfProject_SetOptions(mProject, mTargetType, mStartupObject.c_str(), mDefines.c_str(), mOptLevel, ltoType, 0, 0, flags);
 
 	if (mCELibProject != NULL)
 		BfProject_AddDependency(mProject, mCELibProject);

文件差异内容过多而无法显示
+ 2 - 2
BeefBuild/BeefProj.toml


+ 78 - 4
BeefRT/CMakeLists.txt

@@ -50,6 +50,68 @@ if (${APPLE})
 
     ../BeefySysLib/platform/osx
   )
+elseif (${ANDROID})
+  if (ANDROID_ABI STREQUAL "x86")
+    include_directories(
+      .
+      ../BeefySysLib/  
+      ../BeefySysLib/third_party
+      ../BeefySysLib/third_party/freetype/include
+      ../BeefySysLib/third_party/libffi/i686-pc-linux-gnu/include
+      ../  
+      ../extern
+      ../extern/llvm/include  
+      ../extern/llvm_linux/include
+      ../extern/llvm/lib/Target
+
+      ../BeefySysLib/platform/android
+    )
+  elseif (ANDROID_ABI STREQUAL "x86_64")
+    include_directories(
+      .
+      ../BeefySysLib/  
+      ../BeefySysLib/third_party
+      ../BeefySysLib/third_party/freetype/include
+      ../BeefySysLib/third_party/libffi/x86_64-pc-linux-gnu/include
+      ../  
+      ../extern
+      ../extern/llvm/include  
+      ../extern/llvm_linux/include
+      ../extern/llvm/lib/Target
+
+      ../BeefySysLib/platform/android
+    )
+  elseif (ANDROID_ABI STREQUAL "armeabi-v7a")
+    include_directories(
+      .
+      ../BeefySysLib/  
+      ../BeefySysLib/third_party
+      ../BeefySysLib/third_party/freetype/include
+      ../BeefySysLib/third_party/libffi/arm-unknown-linux-gnu/include
+      ../  
+      ../extern
+      ../extern/llvm/include  
+      ../extern/llvm_linux/include
+      ../extern/llvm/lib/Target
+
+      ../BeefySysLib/platform/android
+    )
+  else()    
+    include_directories(
+      .
+      ../BeefySysLib/  
+      ../BeefySysLib/third_party
+      ../BeefySysLib/third_party/freetype/include
+      ../BeefySysLib/third_party/libffi/aarch64-unknown-linux-gnu/include
+      ../  
+      ../extern
+      ../extern/llvm/include  
+      ../extern/llvm_linux/include
+      ../extern/llvm/lib/Target
+
+      ../BeefySysLib/platform/android
+    )
+  endif()
 else()
   include_directories(
     .
@@ -135,6 +197,7 @@ file(GLOB SRC_FILES
     ../BeefySysLib/util/BeefPerf.cpp
     ../BeefySysLib/util/String.cpp
     ../BeefySysLib/util/UTF8.cpp
+    ../BeefySysLib/util/Hash.cpp    
     ../BeefySysLib/third_party/utf8proc/utf8proc.c    
 )
 
@@ -143,6 +206,11 @@ if (${APPLE})
         ../BeefySysLib/platform/darwin/BFPlatform.cpp
         ../BeefySysLib/platform/darwin/DarwinCommon.cpp
     )
+elseif (${ANDROID})
+  file(GLOB SRC_FILES_OS
+        ../BeefySysLib/platform/android/BFPlatform.cpp
+        ../BeefySysLib/platform/android/AndroidCommon.cpp
+    )
 else()
     file(GLOB SRC_FILES_OS
         ../BeefySysLib/platform/linux/BFPlatform.cpp
@@ -151,15 +219,19 @@ else()
 endif()
 
 # Add library to build.
-add_library(${PROJECT_NAME} SHARED
+add_library(${PROJECT_NAME} STATIC
    ${SRC_FILES}
    ${SRC_FILES_OS}
 )
 
 if (${APPLE})
-    set(TARGET_LIBS_OS "")
+  target_link_libraries(${PROJECT_NAME} pthread ffi)
+elseif (${ANDROID})
+  set(TARGET_LIBS_OS "")
+  #target_link_libraries(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/../BeefySysLib/third_party/libffi/aarch64-unknown-linux-gnu/.libs/libffi.a)
+  #target_link_libraries(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/../BeefySysLib/third_party/libffi/i686-pc-linux-gnu/.libs/libffi.a)
 else()
-    set(TARGET_LIBS_OS "backtrace")
+  target_link_libraries(${PROJECT_NAME} pthread ffi backtrace)
 endif()
 
 # Link with other dependencies.
@@ -167,6 +239,8 @@ if(MSVC)
   target_link_libraries(${PROJECT_NAME} BeefySysLib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib LLVMX86Disassembler.lib LLVMMCDisassembler.lib LLVMSupport.lib LLVMX86Info.lib LLVMX86Utils.lib LLVMX86AsmPrinter.lib LLVMX86Desc.lib %(AdditionalDependencies) LLVMMC.lib LLVMObject.lib LLVMCore.lib LLVMBitReader.lib LLVMAsmParser.lib LLVMMCParser.lib LLVMCodeGen.lib LLVMTarget.lib LLVMX86CodeGen.lib LLVMScalarOpts.lib LLVMInstCombine.lib LLVMSelectionDAG.lib LLVMProfileData.lib LLVMTransformUtils.lib LLVMAnalysis.lib LLVMX86AsmParser.lib LLVMAsmPrinter.lib LLVMBitWriter.lib LLVMVectorize.lib LLVMipo.lib LLVMInstrumentation.lib LLVMDebugInfoDWARF.lib LLVMDebugInfoPDB.lib LLVMDebugInfoCodeView.lib LLVMGlobalISel.lib LLVMBinaryFormat.lib libcurl_a.lib)
 else()
   set(LLVM_LIB "${CMAKE_CURRENT_SOURCE_DIR}/../extern/llvm_linux/lib")
-  target_link_libraries(${PROJECT_NAME} BeefySysLib pthread dl ffi ${TARGET_LIBS_OS}
+  target_link_libraries(${PROJECT_NAME} dl
   )
 endif()
+
+#pthread ffi

+ 2 - 2
BeefRT/rt/Internal.cpp

@@ -230,9 +230,9 @@ void SetErrorString(const char* str)
 	while (true)
 	{
 		const char* prevStr = gErrorString;
-		auto result = ::InterlockedCompareExchangePointer((void* volatile*)&gErrorString, (void*)newStr, (void*)prevStr);
+		auto result = (void*)BfpSystem_InterlockedCompareExchangePtr((uintptr*)&gErrorString, (uintptr)prevStr, (uintptr)newStr);
 		if (result != prevStr)
-			continue;		
+			continue;
 		if (prevStr != NULL)
 			free((void*)prevStr);
 		break;

+ 8 - 0
BeefySysLib/platform/android/AndroidCommon.cpp

@@ -0,0 +1,8 @@
+#include <android/log.h>
+
+#define BFP_PRINTF(...) __android_log_print(ANDROID_LOG_INFO, "Beef", __VA_ARGS__)
+#define BFP_ERRPRINTF(...) __android_log_print(ANDROID_LOG_ERROR, "Beef", __VA_ARGS__)
+
+#define BFP_HAS_PTHREAD_GETATTR_NP
+
+#include "../posix/PosixCommon.cpp"

+ 159 - 0
BeefySysLib/platform/android/AndroidCommon.h

@@ -0,0 +1,159 @@
+#pragma once
+
+#ifdef __LP64__
+#define BF64
+#else
+#define BF32
+#endif
+
+#define BOOST_DETAIL_NO_CONTAINER_FWD
+
+#include <string>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <time.h>
+#include <assert.h>
+#include <sys/time.h>
+//#include <libkern/OSAtomic.h>
+#include <cstdlib>
+#include <unistd.h>
+#include <wchar.h>
+#include <math.h>
+#include <stdarg.h>
+#include <string.h>
+#include <pthread.h>
+#include <wctype.h>
+#include <stddef.h>
+
+//#define offsetof(type, member)  __builtin_offsetof (type, member)
+
+extern "C"
+{
+//#define FFI_BUILDING
+//#include "third_party/libffi/x86_64-apple-darwin12.5.0/include/ffi.h"
+}
+
+#define BF_ENDIAN_LITTLE
+
+#define _NOEXCEPT noexcept
+#define NTAPI
+
+//#define FFI_STDCALL FFI_DEFAULT_ABI
+//#define FFI_THISCALL FFI_DEFAULT_ABI
+//#define FFI_FASTCALL FFI_DEFAULT_ABI
+
+#define INVALID_SOCKET -1
+
+typedef uint64_t uint64;
+typedef uint32_t uint32;
+typedef uint16_t uint16;
+typedef uint8_t uint8;
+typedef int64_t int64;
+typedef int32_t int32;
+typedef int16_t int16;
+typedef int8_t int8;
+typedef unsigned int uint;
+
+//#define BF_PLATFORM_SDL
+
+#define NOP
+//#define BF_NOTHROW throw ()
+//#define BF_NOTHROW noexcept
+#define BF_NOTHROW
+
+#ifdef BF64
+typedef int64 intptr;
+typedef uint64 uintptr;
+#else
+typedef int32 intptr;
+typedef uint32 uintptr;
+#endif
+
+typedef wchar_t* BSTR;
+typedef int HRESULT;
+typedef uint8 BYTE;
+typedef uint16 WORD;
+typedef uint32 DWORD;
+typedef int32 LONG;
+
+typedef pthread_key_t BFTlsKey;
+typedef pthread_t BF_THREADID;
+typedef pthread_t BF_THREADHANDLE;
+
+#define BF_HAS_TLS_DECLSPEC
+#define BF_TLS_DECLSPEC thread_local
+
+//:int64 abs(int64 val);
+
+#define _stricmp stricmp
+#define strnicmp strncasecmp
+
+struct IID
+{
+    unsigned long  Data1;
+    unsigned short Data2;
+    unsigned short Data3;
+    unsigned char  Data4[ 8 ];
+};
+
+typedef void* HANDLE;
+typedef void* HMODULE;
+
+// We only need the stdcall attribute for x32?
+//#define BFSTDCALL __attribute__((stdcall))
+
+//#include "../notwin/NotWin.h"
+
+#ifdef DEBUG
+#define _DEBUG
+#endif
+
+#define NOT_IMPL throw "Unimplemented";
+
+//ARM
+
+#if defined(__x86_64__) || defined(__i386__)
+#define BF_FULL_MEMORY_FENCE() __asm__ __volatile__("mfence": : :"memory")
+#define BF_SPINWAIT_NOP() __asm__ volatile ("pause\n" : : : "memory" );
+#else
+#define BF_FULL_MEMORY_FENCE() __sync_synchronize()
+#define BF_SPINWAIT_NOP() ((void) 0)
+#endif
+
+#define BF_COMPILER_FENCE() __asm__ __volatile__("": : :"memory")
+#define BF_THREAD_YIELD() sched_yield()
+
+#if defined _DEBUG || defined BF_DEBUG_ASSERTS
+#define BF_ASSERT(_Expression) (void)( (!!(_Expression)) || (Beefy::BFFatalError(#_Expression, __FILE__, __LINE__), 0) )
+#else
+#define BF_ASSERT(_Expression) (void)(0)
+#endif
+
+#define BF_ASSERT_REL(_Expression) (void)( (!!(_Expression)) || (Beefy::BFFatalError(#_Expression, __FILE__, __LINE__), 0) )
+#define BF_FATAL(msg) (void) ((Beefy::BFFatalError(msg, __FILE__, __LINE__), 0) )
+
+#if defined _DEBUG || defined BF_DEBUG_ASSERTS
+#define BF_DBG_FATAL(msg) (void) ((Beefy::BFFatalError(msg, __FILE__, __LINE__), 0) )
+#else
+#define BF_DBG_FATAL(msg) 
+#endif
+
+#define BF_NOINLINE __attribute__ ((noinline))
+#define BF_NAKED
+
+#define stricmp strcasecmp
+#define _alloca alloca
+
+#define DIR_SEP_CHAR '/'
+#define DIR_SEP_CHAR_ALT '\\'
+
+static char* itoa(int value, char* str, int base)
+{
+    if (base == 16)
+        sprintf(str, "%X", value);
+    else
+        sprintf(str, "%d", value);
+    return str;
+}

+ 13 - 0
BeefySysLib/platform/android/BFPlatform.cpp

@@ -0,0 +1,13 @@
+#include "Common.h"
+#include "BFPlatform.h"
+//#include <CoreFoundation/CFByteOrder.h>
+//#include <mach/mach_time.h>
+#include <sys/stat.h>
+#include <dlfcn.h>
+#include <wchar.h>
+#include <fcntl.h>
+//#include <mach/clock.h>
+//#include <mach/mach.h>
+#include <time.h>
+#include <dirent.h>
+

+ 24 - 0
BeefySysLib/platform/android/BFPlatform.h

@@ -0,0 +1,24 @@
+#pragma once
+
+#define BFSTDCALL
+
+#include "AndroidCommon.h"
+
+#define BF_PLATFORM_ANDROID
+#define BF_PLATFORM_POSIX
+#define BF_PLATFORM_NAME "BF_PLATFORM_ANDROID"
+
+#define BF_IMPORT extern "C"
+
+#ifdef BFSYSLIB_DYNAMIC
+#define BF_EXPORT extern "C"
+#define BF_CALLTYPE
+#else
+#define BF_EXPORT extern "C"
+#define BF_CALLTYPE
+#define BF_RESOURCES_REL_DIR "../Resources"
+#endif
+
+#define BF_DEBUG_BREAK()
+
+#include "../PlatformInterface.h"

+ 9 - 0
BeefySysLib/platform/android/PlatformApp.h

@@ -0,0 +1,9 @@
+#pragma once
+
+#include "../../HeadlessApp.h"
+
+NS_BF_BEGIN;
+
+typedef HeadlessApp PlatformBFApp;
+
+NS_BF_END;

+ 4 - 3291
BeefySysLib/platform/darwin/DarwinCommon.cpp

@@ -1,3296 +1,9 @@
-#include "Common.h"
-#include "BFPlatform.h"
-#include <sys/stat.h>
+#include <execinfo.h>
 #include <sys/sysctl.h>
-#include <sys/wait.h>
-#include <dlfcn.h>
-#include <wchar.h>
-#include <fcntl.h>
-#include <time.h>
-//#include <link.h>
 #include <dlfcn.h>
-#include <dirent.h>
-#include <syslog.h>
-#include <unistd.h>
-#include <execinfo.h>
-#include <signal.h>
-#include <spawn.h>
-#include "../PlatformInterface.h"
-#include "../PlatformHelper.h"
-#include "../util/CritSect.h"
-#include "../util/Dictionary.h"
-#include "../util/Hash.h"
-#include <execinfo.h>
-//#include "backtrace.h"
-//#include "backtrace-supported.h"
-#include "../third_party/stb/stb_sprintf.h"
-#include <cxxabi.h>
-#include <random>
-
 #include <mach-o/dyld.h>
 
-USING_NS_BF;
-
-
-struct BfpPipeInfo
-{
-    String mPipePath;
-    int mWriteHandle;
-};
-
-struct BfpFile
-{   
-    BfpPipeInfo* mPipeInfo;
-    int mHandle;    
-    bool mNonBlocking;
-    bool mAllowTimeout; 
-    bool mIsStd;
-
-    BfpFile()
-    {
-        mPipeInfo = NULL;
-        mHandle = -1;       
-        mNonBlocking = false;
-        mAllowTimeout = false;      
-        mIsStd = false;
-    }
-
-    BfpFile(int handle)
-    {   
-        mPipeInfo = NULL;
-        mHandle = handle;       
-        mNonBlocking = false;
-        mAllowTimeout = false;      
-        mIsStd = false;
-    }
-
-    ~BfpFile()
-    {
-        delete mPipeInfo;
-    }
-};
-
-BfpTimeStamp BfpToTimeStamp(const timespec& ts)
-{
-    return (int64)(ts.tv_sec * 10000000) + (int64)(ts.tv_nsec / 100) + 116444736000000000;
-}
-
-int gBFPlatformLastError = 0;
-
-uint32 Beefy::BFTickCount()
-{    
-    struct timespec now;
-    if (clock_gettime(CLOCK_MONOTONIC, &now))
-        return 0;
-    return (uint32)((uint64)now.tv_sec * 1000.0 + (uint64)now.tv_nsec / 1000000);
-}
-
-int64 Beefy::EndianSwap(int64 val)
-{
-    return __builtin_bswap64(val);
-}
-
-/*int* GetStdHandle(int32 handleId)
-{
-    if (handleId == STD_INPUT_HANDLE)
-        return (int*)STDIN_FILENO;
-    if (handleId == STD_OUTPUT_HANDLE)
-        return (int*)STDOUT_FILENO;
-    return (int*)STDERR_FILENO;
-}*/
-
-/*int32 GetFileType(HANDLE fileHandle)
-{
-    if (isatty(file->mHandleHandle))
-        return FILE_TYPE_CHAR;
-    return FILE_TYPE_DISK;
-}*/
-
-/*bool WriteFile(HANDLE hFile, void* lpBuffer, uint32 nNumberOfBytesToWrite, uint32* lpNumberOfBytesWritten, OVERLAPPED* lpOverlapped)
-{
-#ifdef BF_PLATFORM_IOS
-    int logType = -1;
-    if (hFile == (int*)STDOUT_FILENO)
-        logType = LOG_WARNING;
-    else if (hFile == (int*)STDERR_FILENO)
-        logType = LOG_ERR;
-    
-    if (logType != -1)
-    {
-        static std::string strOut;
-        strOut.resize(nNumberOfBytesToWrite);
-        memcpy(&strOut[0], lpBuffer, nNumberOfBytesToWrite);
-        if ((strOut[0] != '\r') && (strOut[0] != '\n'))
-            syslog(LOG_WARNING, "%s", strOut.c_str());
-    }
-#endif
-    
-    int writeCount = (int)::write((int)(intptr)hFile, lpBuffer, nNumberOfBytesToWrite);
-    if (writeCount == -1)
-    {
-        //TODO: set gBFPlatformLastError
-        lpNumberOfBytesWritten = 0;
-        return false;
-    }
-    
-    *lpNumberOfBytesWritten = (uint32)writeCount;
-    return true;
-}*/
-
-int64 Beefy::GetFileTimeWrite(const StringImpl& path)
-{
-    struct stat statbuf = {0};
-    int result = stat(path.c_str(), &statbuf);
-    if (result == -1)
-        return 0;
-    
-    //int64 fileTime = 0;
-    //BFSystemTimeToFileTime(statbuf.st_mtime, 0, &fileTime);
-    return statbuf.st_mtime;
-}
-
-/*DWORD GetTimeZoneInformation(TIME_ZONE_INFORMATION* lpTimeZoneInformation)
-{
-    std::wstring tzName0 = Beefy::UTF8Decode(tzname[0]);
-    std::wstring tzName1 = Beefy::UTF8Decode(tzname[1]);
-    
-    bool isDST = false;
-    
-    time_t timeNow;
-    time(&timeNow);
-    tm tmNow = *gmtime(&timeNow);
-    isDST = tmNow.tm_isdst;
-    
-    struct tm checkTM;
-    memset(&checkTM, 0, sizeof(tm));
-    checkTM.tm_mday = 1;
-    checkTM.tm_year = tmNow.tm_year;
-    time_t checkTime = mktime(&checkTM);
-    
-    time_t lastOffset = 0;
-    time_t minOffset = 0;
-    time_t maxOffset = 0;
-    
-    for (int pass = 0; pass < 2; pass++)
-    {
-        int searchDir = 60*60*24;
-        int thresholdCount = 0;
-        
-        while (true)
-        {
-            checkTime += searchDir;
-            
-            tm checkTM = *gmtime(&checkTime);
-            
-            if (checkTM.tm_year != tmNow.tm_year)
-                break; // No DST
-            
-            mktime(&checkTM);
-            
-            time_t offset = checkTM.tm_gmtoff;
-            if (lastOffset != offset)
-            {
-                if (thresholdCount == 0)
-                {
-                    minOffset = offset;
-                    maxOffset = offset;
-                }
-                else if (thresholdCount == 3)
-                {
-                    SYSTEMTIME* sysTimeP = (offset == minOffset) ?
-                    &lpTimeZoneInformation->StandardDate :
-                    &lpTimeZoneInformation->DaylightDate;
-                    
-                    if (offset == minOffset)
-                        tzName0 = Beefy::UTF8Decode(checkTM.tm_zone);
-                    else
-                        tzName1 = Beefy::UTF8Decode(checkTM.tm_zone);
-                    
-                    sysTimeP->wDay = 0;
-                    sysTimeP->wDayOfWeek = 0;
-                    sysTimeP->wYear = checkTM.tm_year + 1900;
-                    sysTimeP->wMonth = checkTM.tm_mon;
-                    sysTimeP->wDay = checkTM.tm_mday + 1;
-                    sysTimeP->wHour = checkTM.tm_hour;
-                    sysTimeP->wMinute = checkTM.tm_min;
-                    sysTimeP->wSecond = checkTM.tm_sec;
-                    sysTimeP->wMilliseconds = 0;
-                    
-                    break;
-                }
-                else
-                {
-                    if (thresholdCount == 1)
-                        searchDir /= -24;
-                    else
-                        searchDir /= -60;
-                    minOffset = std::min(minOffset, offset);
-                    maxOffset = std::max(maxOffset, offset);
-                }
-                thresholdCount++;
-                lastOffset = offset;
-            }
-        }
-    }
-    
-    wcsncpy(lpTimeZoneInformation->StandardName, tzName0.c_str(), 32);
-    wcsncpy(lpTimeZoneInformation->DaylightName, tzName1.c_str(), 32);
-    
-    lpTimeZoneInformation->DaylightBias = (int32)maxOffset;
-    lpTimeZoneInformation->StandardBias = (int32)minOffset;
-    
-    if (minOffset == maxOffset)
-        return 0;
-    return isDST ? 2 : 1;
-}*/
-
-bool Beefy::FileExists(const StringImpl& path, String* outActualName)
-{
-    struct stat statbuf = {0};
-    int result = stat(path.c_str(), &statbuf);
-    if (result != 0)
-        return false;
-    return !S_ISDIR(statbuf.st_mode);
-}
-
-bool Beefy::DirectoryExists(const StringImpl& path, String* outActualName)
-{
-    struct stat statbuf = {0};
-    int result = stat(path.c_str(), &statbuf);
-    if (result != 0)
-        return false;
-    return S_ISDIR(statbuf.st_mode);
-}
-
-uint64 Beefy::BFGetTickCountMicro()
-{
-    struct timespec now;
-    if (clock_gettime(CLOCK_MONOTONIC, &now))
-        return 0;
-    return ((uint64)now.tv_sec * 1000000.0 + (uint64)now.tv_nsec / 1000);
-}
-
-uint64 Beefy::BFGetTickCountMicroFast()
-{
-    return BFGetTickCountMicro();
-}
-
-/*
-int64 abs(int64 val)
-{
-    return llabs(val);
-}
-*/
-void mkdir(const char* path)
-{
-    mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
-}
-
-typedef void(*CrashInfoFunc)();
-
-static CritSect gSysCritSect;
-static String gCrashInfo;
-static Array<CrashInfoFunc> gCrashInfoFuncs;
-
-static String gCmdLine;
-static String gExePath;
-
-typedef struct _Unwind_Context _Unwind_Context;   // opaque
-
-typedef enum {
-  _URC_NO_REASON = 0,
-  _URC_OK = 0,
-  _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
-  _URC_FATAL_PHASE2_ERROR = 2,
-  _URC_FATAL_PHASE1_ERROR = 3,
-  _URC_NORMAL_STOP = 4,
-  _URC_END_OF_STACK = 5,
-  _URC_HANDLER_FOUND = 6,
-  _URC_INSTALL_CONTEXT = 7,
-  _URC_CONTINUE_UNWIND = 8,
-  _URC_FAILURE = 9
-} _Unwind_Reason_Code;
-
-typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn)(struct _Unwind_Context *, void *);
-extern "C" _Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn, void *);
-extern "C" uintptr_t _Unwind_GetIP(struct _Unwind_Context *context);
-
-static String gUnwindExecStr;
-static int gUnwindIdx = 0;
-
-static _Unwind_Reason_Code UnwindHandler(struct _Unwind_Context* context, void* ref)
-{   
-    gUnwindIdx++;
-    if (gUnwindIdx < 2)
-        return _URC_NO_REASON;
-
-    dl_info dyldInfo;
-    void* addr = (void*)_Unwind_GetIP(context);
-    gUnwindExecStr += StrFormat(" %p", addr);
-    return _URC_NO_REASON;
-}
-
-static bool FancyBacktrace()
-{
-    gUnwindExecStr += StrFormat("atos -p %d", getpid());    
-    _Unwind_Backtrace(&UnwindHandler, NULL);
-    return system(gUnwindExecStr.c_str()) == 0;
-}
-
-static void Crashed()
-{
-    //
-    {
-        AutoCrit autoCrit(gSysCritSect);
-
-        String debugDump;
-
-        debugDump += "**** FATAL APPLICATION ERROR ****\n";
-
-        for (auto func : gCrashInfoFuncs)
-            func();
-
-        if (!gCrashInfo.IsEmpty())
-        {
-            debugDump += gCrashInfo;
-            debugDump += "\n";
-        }
-
-        fprintf(stderr, "%s", debugDump.c_str());
-    }
-
-    if (!FancyBacktrace())
-    {
-        void* array[64];
-        size_t size;
-        char** strings;
-        size_t i;
-
-        size = backtrace(array, 64);
-        strings = backtrace_symbols(array, size);
-
-        for (i = 0; i < size; i++)
-            fprintf(stderr, "%s\n", strings[i]);
-
-        free(strings);
-    }
-
-    exit(1);
-}
-
-static void SigHandler(int sig)
-{
-    //printf("SigHandler paused...\n"); 
-
-    const char* sigName = NULL;
-    switch (sig)
-    {
-    case SIGFPE:
-        sigName = "SIGFPE";
-        break;
-    case SIGSEGV:
-        sigName = "SIGSEGV";
-        break;
-    case SIGABRT:
-        sigName = "SIGABRT";
-        break;
-    case SIGILL:
-        sigName = "SIGILL";
-        break;
-    }
-
-    if (sigName != NULL)
-        gCrashInfo += StrFormat("Signal: %s\n", sigName);
-    else
-        gCrashInfo += StrFormat("Signal: %d\n", sig);
-    Crashed();
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpSystem_Init(int version, BfpSystemInitFlags flags)
-{
-    if (version != BFP_VERSION)
-    {
-        BfpSystem_FatalError(StrFormat("Bfp build version '%d' does not match requested version '%d'", BFP_VERSION, version).c_str(), "BFP FATAL ERROR");
-    }
-
-    signal(SIGSEGV, SigHandler);
-    signal(SIGFPE, SigHandler);
-    signal(SIGABRT, SigHandler);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpSystem_SetCommandLine(int argc, char** argv)
-{ 
-    char* relPath = argv[0];
-
-    char* cwd = getcwd(NULL, 0);
-    gExePath = GetAbsPath(relPath, cwd);
-    
-    free(cwd);
-
-    for (int i = 0; i < argc; i++)
-    {
-        if (i != 0)
-            gCmdLine.Append(' ');
-
-        String arg = argv[i];
-        if ((arg.Contains(' ')) || (arg.Contains('\"')))
-        {
-            arg.Replace("\"", "\\\"");
-            gCmdLine.Append("\"");
-            gCmdLine.Append(arg);
-            gCmdLine.Append("\"");
-        }
-        else
-            gCmdLine.Append(arg);
-    }
-}
-
-
-BFP_EXPORT void BFP_CALLTYPE BfpSystem_SetCrashReportKind(BfpCrashReportKind crashReportKind)
-{
-
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpSystem_AddCrashInfoFunc(BfpCrashInfoFunc crashInfoFunc)
-{
-    AutoCrit autoCrit(gSysCritSect);
-    gCrashInfoFuncs.Add(crashInfoFunc);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpSystem_AddCrashInfo(const char* str) // Can do at any time, or during CrashInfoFunc callbacks
-{
-    AutoCrit autoCrit(gSysCritSect);
-    gCrashInfo.Append(str);
-}
-
-void BfpSystem_Shutdown()
-{
-
-}
-
-BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_TickCount()
-{
-    return Beefy::BFTickCount();
-}
-
-BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpSystem_GetTimeStamp()
-{
-    struct timeval tv;
-    BfpTimeStamp result = 11644473600LL;
-    gettimeofday(&tv, NULL);
-    result += tv.tv_sec;
-    result *= 10000000LL;
-    result += tv.tv_usec * 10;
-    return result;
-}
-
-BFP_EXPORT uint16 BFP_CALLTYPE BfpSystem_EndianSwap16(uint16 val)
-{
-    return __builtin_bswap16(val);
-}
-
-BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_EndianSwap32(uint32 val)
-{
-    return __builtin_bswap32(val);
-}
-
-BFP_EXPORT uint64 BFP_CALLTYPE BfpSystem_EndianSwap64(uint64 val)
-{
-    return __builtin_bswap64(val);
-}
-
-BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_InterlockedExchange32(uint32* ptr, uint32 val)
-{
-    // __sync_lock_test_and_set only has Acquire semantics, so we need a __sync_synchronize to enforce a full barrier
-    uint32 prevVal = __sync_lock_test_and_set(ptr, val);
-    __sync_synchronize();
-    return prevVal; 
-}
-
-BFP_EXPORT uint64 BFP_CALLTYPE BfpSystem_InterlockedExchange64(uint64* ptr, uint64 val)
-{
-    // __sync_lock_test_and_set only has Acquire semantics, so we need a __sync_synchronize to enforce a full barrier
-    uint64 prevVal = __sync_lock_test_and_set(ptr, val);
-    __sync_synchronize();
-    return prevVal; 
-}
-
-BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_InterlockedExchangeAdd32(uint32* ptr, uint32 val)
-{
-    return __sync_fetch_and_add(ptr, val);
-}
-
-BFP_EXPORT uint64 BFP_CALLTYPE BfpSystem_InterlockedExchangeAdd64(uint64* ptr, uint64 val)
-{
-    return __sync_fetch_and_add(ptr, val);
-}
-
-BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_InterlockedCompareExchange32(uint32* ptr, uint32 oldVal, uint32 newVal)
-{
-    return __sync_val_compare_and_swap(ptr, oldVal, newVal);
-}
-
-BFP_EXPORT uint64 BFP_CALLTYPE BfpSystem_InterlockedCompareExchange64(uint64* ptr, uint64 oldVal, uint64 newVal)
-{
-    return __sync_val_compare_and_swap(ptr, oldVal, newVal);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpSystem_FatalError(const char* error, const char* title)
-{
-    fprintf(stderr, "%s\n", error);
-    fflush(stderr);
-    Crashed();
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpSystem_GetCommandLine(char* outStr, int* inOutStrSize, BfpSystemResult* outResult)
-{
-    TryStringOut(gCmdLine, outStr, inOutStrSize, (BfpResult*)outResult);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpSystem_GetExecutablePath(char* outStr, int* inOutStrSize, BfpSystemResult* outResult)
-{
-    //printf("Setting BfpSystem_GetExecutablePath %s %p\n", gExePath.c_str(), &gExePath);
-
-    if (gExePath.IsEmpty())
-    {
-        char path[4096];
-        uint32_t size = sizeof(path);
-        if (_NSGetExecutablePath(path, &size) == 0)
-            gExePath = path;
-
-        // When when running with a './file', we end up with an annoying '/./' in our path
-        gExePath.Replace("/./", "/");        
-    }
-
-    TryStringOut(gExePath, outStr, inOutStrSize, (BfpResult*)outResult);
-}
-
-extern char **environ;
-
-BFP_EXPORT void BFP_CALLTYPE BfpSystem_GetEnvironmentStrings(char* outStr, int* inOutStrSize, BfpSystemResult* outResult)
-{
-    String env;
-
-    char** envPtr = environ;
-    while (true)
-    {
-        char* envStr = *envPtr;
-        env.Append(envStr, strlen(envStr) + 1);
-        ++envPtr;
-    }
-
-    TryStringOut(env, outStr, inOutStrSize, (BfpResult*)outResult);
-}
-
-BFP_EXPORT int BFP_CALLTYPE BfpSystem_GetNumLogicalCPUs(BfpSystemResult* outResult)
-{   
-    OUTRESULT(BfpSystemResult_Ok);
-    int count = 1;
-    size_t count_len = sizeof(count);
-    sysctlbyname("hw.logicalcpu", &count, &count_len, NULL, 0);
-    return count;
-}
-
-BFP_EXPORT int64 BFP_CALLTYPE BfpSystem_GetCPUTick()
-{
-    return 10000000;
-}
-
-BFP_EXPORT int64 BFP_CALLTYPE BfpSystem_GetCPUTickFreq()
-{
-    struct timespec now;
-    clock_gettime(CLOCK_MONOTONIC, &now);
-    return (now.tv_sec * 10000000LL) + now.tv_nsec / 100;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpSystem_CreateGUID(BfpGUID* outGuid)
-{
-//  uuid_t guid;    
-//  uuid_generate(guid);    
-//  BfpGUID bfpGuid;
-//  memcpy(&bfpGuid, guid, 16);
-//  return bfpGuid;
-
-    uint8* ptr = (uint8*)outGuid;
-
-    std::random_device rd;
-    std::mt19937 gen(rd());
-    std::uniform_int_distribution<uint8> dis(0, 255);
-    for (int i = 0; i < 16; i++)
-        ptr[i] = dis(gen);
-
-    // variant must be 10xxxxxx
-    ptr[8] &= 0xBF;
-    ptr[8] |= 0x80;
-
-    // version must be 0100xxxx
-    ptr[6] &= 0x4F;
-    ptr[6] |= 0x40;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpSystem_GetComputerName(char* outStr, int* inOutStrSize, BfpSystemResult* outResult)
-{
-    char hostName[1024];
-    gethostname(hostName, 1024);
-    TryStringOut(hostName, outStr, inOutStrSize, (BfpResult*)outResult);
-}
-
-// BfpProcess
-
-BFP_EXPORT intptr BFP_CALLTYPE BfpProcess_GetCurrentId()
-{
-    return getpid();
-}
-
-BFP_EXPORT bool BFP_CALLTYPE BfpProcess_IsRemoteMachine(const char* machineName)
-{
-    return false;
-}
-
-BFP_EXPORT BfpProcess* BFP_CALLTYPE BfpProcess_GetById(const char* machineName, int processId, BfpProcessResult* outResult)
-{
-    NOT_IMPL;
-    return NULL;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpProcess_Enumerate(const char* machineName, BfpProcess** outProcesses, int* inOutProcessesSize, BfpProcessResult* outResult)
-{
-    NOT_IMPL;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpProcess_Release(BfpProcess* process)
-{
-    NOT_IMPL;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpProcess_GetMainWindowTitle(BfpProcess* process, char* outTitle, int* inOutTitleSize, BfpProcessResult* outResult)
-{
-    NOT_IMPL;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpProcess_GetProcessName(BfpProcess* process, char* outName, int* inOutNameSize, BfpProcessResult* outResult)
-{
-    NOT_IMPL;
-}
-
-BFP_EXPORT int BFP_CALLTYPE BfpProcess_GetProcessId(BfpProcess* process)
-{
-    NOT_IMPL;
-    return 0;
-}
-
-// BfpSpawn
-
-struct BfpSpawn
-{
-    int mPid;
-    bool mExited;
-    int mStatus;
-    int mStdInFD;
-    int mStdOutFD;
-    int mStdErrFD;
-};
-
-BFP_EXPORT BfpSpawn* BFP_CALLTYPE BfpSpawn_Create(const char* inTargetPath, const char* args, const char* workingDir, const char* env, BfpSpawnFlags flags, BfpSpawnResult* outResult)
-{
-    Beefy::Array<Beefy::StringView> stringViews;
-
-    //printf("Executing: %s %s %x\n", inTargetPath, args, flags);
-
-    if ((workingDir != NULL) && (workingDir[0] != 0))
-    {
-        if (chdir(workingDir) != 0)
-        {
-            //printf("CHDIR failed %s\n", workingDir);
-            OUTRESULT(BfpSpawnResult_UnknownError);
-            return NULL;
-        }
-    }
-
-    String newArgs;
-    String tempFileName;
-
-    if ((flags & BfpSpawnFlag_UseArgsFile) != 0)
-    {
-        char tempFileNameStr[256];
-        int size = 256;
-        BfpFileResult fileResult;
-        BfpFile_GetTempFileName(tempFileNameStr, &size, &fileResult);
-        if (fileResult == BfpFileResult_Ok)
-        {
-            tempFileName = tempFileNameStr;
-
-            BfpFileResult fileResult;
-            BfpFile* file = BfpFile_Create(tempFileNameStr, BfpFileCreateKind_CreateAlways, BfpFileCreateFlag_Write, BfpFileAttribute_Normal, &fileResult);
-            if (file == NULL)
-            {
-                OUTRESULT(BfpSpawnResult_TempFileError);
-                return NULL;
-            }
-
-            if ((flags & BfpSpawnFlag_UseArgsFile_Native) != 0)
-            {
-                UTF16String wStr = UTF8Decode(args);
-
-                if ((flags & BfpSpawnFlag_UseArgsFile_BOM) != 0)
-                {
-                    uint8 bom[2] = { 0xFF, 0xFE };
-                    BfpFile_Write(file, bom, 2, -1, NULL);
-                }
-
-                BfpFile_Write(file, wStr.c_str(), wStr.length() * 2, -1, NULL);
-            }
-            else
-                BfpFile_Write(file, args, strlen(args), -1, NULL);
-            BfpFile_Release(file);
-
-            newArgs.Append("@");
-            newArgs.Append(tempFileName);
-            if (newArgs.Contains(' '))
-            {
-                newArgs.Insert(0, '\"');
-                newArgs.Append('\"');
-            }
-
-            args = newArgs.c_str();
-        }
-    }
-
-    int32 firstCharIdx = -1;
-    bool inQuote = false;
-
-    String targetPath = inTargetPath;
-    String verb;
-    if ((flags & BfpSpawnFlag_UseShellExecute) != 0)
-    {
-        String target = targetPath;
-        int barPos = (int)target.IndexOf('|');
-        if (barPos != -1)
-        {
-            verb = targetPath.Substring(barPos + 1);
-            targetPath.RemoveToEnd(barPos);
-        }               
-    }
-
-    int32 i = 0;
-    for ( ; true; i++)
-    {
-        char c = args[i];
-        if (c == '\0')
-            break;
-        if ((c == ' ') && (!inQuote))
-        {
-            if (firstCharIdx != -1)
-            {
-                stringViews.Add(Beefy::StringView(args, firstCharIdx, i - firstCharIdx));
-                firstCharIdx = -1;
-            }
-        }
-        else
-        {
-            if (firstCharIdx == -1)
-                firstCharIdx = i;
-            if (c == '"')
-                inQuote = !inQuote;
-            else if ((inQuote) && (c == '\\'))
-            {
-                c = args[i + 1];
-                if (c == '"')
-                    i++;
-            }
-        }
-    }
-    if (firstCharIdx != -1)
-        stringViews.Add(Beefy::StringView(args, firstCharIdx, i - firstCharIdx));
-
-    Beefy::Array<char*> argvArr;
-
-    if ((flags & BfpSpawnFlag_ArgsIncludesTarget) == 0)
-        argvArr.Add(strdup(targetPath.c_str()));
-
-    for (int32 i = 0; i < (int32)stringViews.size(); i++)
-    {
-        Beefy::StringView stringView = stringViews[i];
-        char* str = NULL;
-        for (int32 pass = 0; pass < 2; pass++)
-        {
-            char* strPtr = str;
-
-            int32 strPos = 0;
-            for (int32 char8Idx = 0; char8Idx < stringView.mLength; char8Idx++)
-            {
-                char c = stringView.mPtr[char8Idx];
-                if (c == '"')
-                    inQuote = !inQuote;
-                else
-                {
-                    if ((inQuote) && (c == '\\') && (char8Idx < stringView.mLength - 1))
-                    {
-                        char nextC = stringView.mPtr[char8Idx + 1];
-                        if (nextC == '"')
-                        {
-                            c = nextC;
-                            char8Idx++;
-                        }
-                    }
-                    if (strPtr != NULL)
-                        *(strPtr++) = c;
-                    strPos++;
-                }
-            }
-            if (pass == 0)
-                str = (char*)malloc(strPos + 1);
-            else
-                *(strPtr++) = 0;
-        }
-
-        argvArr.Add(str);
-    }
-    argvArr.Add(NULL);
-
-    char** argv = NULL;
-
-    //pid_t pid = 0;
-    //int status = posix_spawn(&pid, targetPath, NULL, NULL, &argvArr[0], environ);
-
-    Beefy::Array<char*> envArr;
-    if (env != NULL)
-    {
-        char* envPtr = (char*)env;
-        while (true)
-        {
-            if (*envPtr == 0)
-                break;
-
-            envArr.Add(envPtr);
-            envPtr += strlen(envPtr) + 1;
-        }
-    }
-    envArr.Add(NULL);
-
-    int stdInFD[2];
-    int stdOutFD[2];
-    int stdErrFD[2];
-
-    bool failed = false;
-    if ((flags & BfpSpawnFlag_RedirectStdInput) != 0)
-        if (pipe(stdInFD) != 0)
-            failed = true;
-    if ((flags & BfpSpawnFlag_RedirectStdOutput) != 0)
-        if (pipe(stdOutFD) != 0)
-            failed = true;
-    if ((flags & BfpSpawnFlag_RedirectStdError) != 0)
-        if (pipe(stdErrFD) != 0)
-            failed = true;
-    if (failed)
-    {
-        //printf("Pipe failed\n");
-        OUTRESULT(BfpSpawnResult_UnknownError);
-        return NULL;
-    }
-
-    BfpSpawn* spawn;
-    pid_t pid = fork();
-    if (pid == -1) // Error
-    {
-        OUTRESULT(BfpSpawnResult_UnknownError);
-        return NULL;
-    }
-    else if (pid == 0) // Child
-    {
-        if ((flags & BfpSpawnFlag_RedirectStdInput) != 0)
-            while ((dup2(stdInFD[0], STDIN_FILENO) == -1) && (errno == EINTR)) {}
-        if ((flags & BfpSpawnFlag_RedirectStdOutput) != 0)
-            while ((dup2(stdOutFD[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {}
-        if ((flags & BfpSpawnFlag_RedirectStdError) != 0)
-            while ((dup2(stdErrFD[1], STDERR_FILENO) == -1) && (errno == EINTR)) {}
-
-        // If successful then this shouldn't return at all:
-        int result;
-
-        if (env != NULL)
-            result = execve(targetPath.c_str(), (char* const*)&argvArr[0], (char* const*)&envArr[0]);
-        else
-            result = execv(targetPath.c_str(), (char* const*)&argvArr[0]);
-
-        printf("Couldn't execute %s\n", targetPath.c_str());
-
-        exit(-1);
-    }
-    else // Parent
-    {
-        spawn = new BfpSpawn();
-
-        if ((flags & BfpSpawnFlag_RedirectStdInput) != 0)
-        {
-            spawn->mStdInFD = stdInFD[1];
-            close(stdInFD[0]);
-        }
-        else
-            spawn->mStdInFD = 0;
-
-        if ((flags & BfpSpawnFlag_RedirectStdOutput) != 0)
-        {
-            spawn->mStdOutFD = stdOutFD[0];
-            close(stdOutFD[1]);
-        }
-        else
-            spawn->mStdOutFD = 0;
-
-        if ((flags & BfpSpawnFlag_RedirectStdError) != 0)
-        {
-            spawn->mStdErrFD = stdErrFD[0];
-            close(stdErrFD[1]);
-        }
-        else
-            spawn->mStdErrFD = 0;
-    }
-
-    for (auto val : argvArr)
-        free(val);
-
-    //printf("Spawn pid:%d status:%d\n", pid, status);
-    spawn->mPid = pid;
-    spawn->mExited = false;
-    spawn->mStatus = 0;
-
-    return spawn;
-}
-
-void BfpSpawn_Release(BfpSpawn* spawn)
-{
-    // We don't support 'detaching' currently- this can create zombie processes since we
-    //  don't have a reaper strategy
-    BfpSpawn_WaitFor(spawn, -1, NULL, NULL);
-
-    delete spawn;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpSpawn_GetStdHandles(BfpSpawn* spawn, BfpFile** outStdIn, BfpFile** outStdOut, BfpFile** outStdErr)
-{
-    if (outStdIn != NULL)
-    {
-        *outStdIn = new BfpFile(spawn->mStdInFD);
-        spawn->mStdInFD = 0;
-    }
-
-    if (outStdOut != NULL)
-    {
-        *outStdOut = new BfpFile(spawn->mStdOutFD);
-        spawn->mStdOutFD = 0;
-    }
-
-    if (outStdErr != NULL)
-    {
-        *outStdErr = new BfpFile(spawn->mStdErrFD);
-        spawn->mStdErrFD = 0;
-    }
-}
-
-bool BfpSpawn_WaitFor(BfpSpawn* spawn, int waitMS, int* outExitCode, BfpSpawnResult* outResult)
-{
-    OUTRESULT(BfpSpawnResult_Ok);
-    if (!spawn->mExited)
-    {
-        int flags = 0;
-        if (waitMS != -1)
-        {
-            flags = WNOHANG;
-        }
-        //TODO: Implement values other than 0 or -1 for waitMS?
-
-        pid_t result = waitpid(spawn->mPid, &spawn->mStatus, flags);
-        if (result != spawn->mPid)
-            return false;
-
-        spawn->mExited = true;
-    }
-
-    if (!WIFEXITED(spawn->mStatus) && !WIFSIGNALED(spawn->mStatus))
-        return false;
-
-    if (outExitCode != NULL)
-        *outExitCode = WEXITSTATUS(spawn->mStatus);
-    return true;
-}
-
-// BfpFileWatcher
-
-BFP_EXPORT BfpFileWatcher* BFP_CALLTYPE BfpFileWatcher_WatchDirectory(const char* path, BfpDirectoryChangeFunc callback, BfpFileWatcherFlags flags, void* userData, BfpFileResult* outResult)
-{
-    NOT_IMPL;
-    return NULL;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFileWatcher_Release(BfpFileWatcher* fileWatcher)
-{
-    NOT_IMPL;
-}
-
-// BfpThread
-
-struct BfpThread
-{
-    bool mPThreadReleased;
-    BfpThreadStartProc mStartProc;
-    void* mThreadParam;
-    BfpEvent* mDoneEvent;
-
-    pthread_t mPThread;
-    int mRefCount;
-    int mPriority;   
-
-    BfpThread()
-    {
-    }
-
-    void Release()
-    {
-        int refCount = __sync_fetch_and_sub(&mRefCount, 1) - 1;
-        if (refCount == 0)
-            delete this;
-    }
-};
-
-struct BfpThreadInfo
-{
-    intptr mStackBase;
-    int mStackLimit;
-};
-
-static __thread BfpThread* gCurrentThread;
-static __thread BfpThreadInfo gCurrentThreadInfo;
-
-void* ThreadFunc(void* threadParam)
-{
-    BfpThread* thread = (BfpThread*)threadParam;
-    gCurrentThread = thread;
-    thread->mStartProc(thread->mThreadParam);
-    BfpEvent_Set(thread->mDoneEvent, true);
-    thread->Release();
-    return NULL;
-}
-
-BFP_EXPORT BfpThread* BFP_CALLTYPE BfpThread_Create(BfpThreadStartProc startProc, void* threadParam, intptr stackSize, BfpThreadCreateFlags flags, BfpThreadId* outThreadId)
-{
-    BfpThread* thread = new BfpThread();
-    thread->mPThreadReleased = false;
-    thread->mStartProc = startProc;
-    thread->mThreadParam = threadParam;
-    thread->mRefCount = 2;
-    thread->mPriority = 0;
-    thread->mDoneEvent = BfpEvent_Create(BfpEventFlag_None);
-
-    BF_ASSERT(sizeof(pthread_t) <= sizeof(void*));    
-    pthread_attr_t params;
-    pthread_attr_init(&params);
-    pthread_attr_setstacksize(&params, stackSize);
-    //pthread_attr_setdetachstate(&params,PTHREAD_CREATE_DETACHED);
-
-    pthread_create(&thread->mPThread, &params, ThreadFunc, (void*)thread);
-
-    pthread_attr_destroy(&params);
-    
-    if (outThreadId != NULL)
-        *outThreadId = (BfpThreadId)thread->mPThread;
-
-    return thread;
-}
-
-#define FIXTHREAD() \
-    pthread_t pt; \
-    if (((intptr)thread & 1) != 0) \
-    { \
-        pt = (pthread_t)((intptr)thread & ~3); \
-        thread = NULL; \
-    } else \
-    pt = thread->mPThread
-
-BFP_EXPORT void BFP_CALLTYPE BfpThread_Release(BfpThread* thread)
-{
-    FIXTHREAD();
-    if (thread == NULL)
-        return;
-
-    BfpEvent_Release(thread->mDoneEvent);
-    if (!thread->mPThreadReleased)
-    {
-        pthread_detach(thread->mPThread);
-        thread->mPThreadReleased = true;
-    }
-    thread->Release();
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpThread_SetName(BfpThread* thread, const char* name, BfpThreadResult* outResult)
-{
-    OUTRESULT(BfpThreadResult_Ok);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpThread_GetName(BfpThread* thread, char* outName, int* inOutNameSize, BfpThreadResult* outResult)
-{
-    String str = "";
-    TryStringOut(str, outName, inOutNameSize, (BfpResult*)outResult);
-}
-
-BFP_EXPORT BfpThread* BFP_CALLTYPE BfpThread_GetCurrent()
-{
-    if (gCurrentThread == NULL)
-    {
-        // Not a "true" BfpThread, this is either the main thread or a thread we didn't create
-        return (BfpThread*)((intptr)pthread_self() | 1);
-    }
-    return gCurrentThread;
-}
-
-BFP_EXPORT BfpThreadId BFP_CALLTYPE BfpThread_GetCurrentId()
-{
-    if (gCurrentThread == NULL)
-    {
-        return (BfpThreadId)((intptr)pthread_self());
-    }
-    return (BfpThreadId)gCurrentThread->mPThread;
-}
-
-BFP_EXPORT bool BFP_CALLTYPE BfpThread_WaitFor(BfpThread* thread, int waitMS)
-{
-    FIXTHREAD();
-
-    if (waitMS == -1)
-    {
-        pthread_join(pt, NULL);
-        thread->mPThreadReleased = true;
-        return true;
-    }
-
-    if (thread == NULL)
-        BF_FATAL("Invalid thread with non-infinite wait");
-    return BfpEvent_WaitFor(thread->mDoneEvent, waitMS);    
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpSpawn_Kill(BfpSpawn* spawn, int exitCode, BfpKillFlags killFlags, BfpSpawnResult* outResult)
-{
-    //TODO: Implement
-    OUTRESULT(BfpSpawnResult_UnknownError);
-}
-
-BFP_EXPORT BfpThreadPriority BFP_CALLTYPE BfpThread_GetPriority(BfpThread* thread, BfpThreadResult* outResult)
-{
-    FIXTHREAD();
-
-    OUTRESULT(BfpThreadResult_Ok);
-    if (thread == NULL)
-        return (BfpThreadPriority)0;
-
-    return (BfpThreadPriority)thread->mPriority;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpThread_SetPriority(BfpThread* thread, BfpThreadPriority threadPriority, BfpThreadResult* outResult)
-{
-    // In effect, we have two 'nice' settings: 0 (normal) or 10 (low)
-    //  High-priority settings just don't do anything
-    //pid_t tid = syscall(SYS_gettid);
-    //int ret = setpriority(PRIO_PROCESS, tid, -std::min(nPriority, 0) * 10);
-    OUTRESULT(BfpThreadResult_Ok);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpThread_Suspend(BfpThread* thread, BfpThreadResult* outResult)
-{
-    NOT_IMPL;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpThread_Resume(BfpThread* thread, BfpThreadResult* outResult)
-{
-    NOT_IMPL;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpThread_GetIntRegisters(BfpThread* thread, intptr* outStackPtr, intptr* outIntRegs, int* inOutIntRegCount, BfpThreadResult* outResult)
-{
-    NOT_IMPL;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpThread_GetStackInfo(BfpThread* thread, intptr* outStackBase, int* outStackLimit, BfpThreadResult* outResult)
-{
-    // USE mach_vm_region_info ??
-    // https://github.com/dmlloyd/openjdk/blob/2f680fec58a8ffb818a48f542e493306d1113c69/src/hotspot/os_cpu/bsd_x86/os_bsd_x86.cpp#L902-L961
-
-    /*if (gCurrentThreadInfo.mStackBase == 0)
-    {
-        void* stackBase = 0;
-        size_t stackLimit = 0;
-
-        pthread_attr_t attr;
-        pthread_getattr_np(pthread_self(), &attr);
-        pthread_attr_getstack(&attr, &stackBase, &stackLimit);
-
-        gCurrentThreadInfo.mStackBase = (intptr)stackBase + stackLimit;
-        gCurrentThreadInfo.mStackLimit = (int)stackLimit;
-        pthread_attr_destroy(&attr);
-    }
-
-    *outStackBase = gCurrentThreadInfo.mStackBase;
-    *outStackLimit = gCurrentThreadInfo.mStackLimit;
-
-    OUTRESULT(BfpThreadResult_Ok);*/
-    OUTRESULT(BfpThreadResult_UnknownError);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpThread_Sleep(int sleepMS)
-{
-    usleep(sleepMS * 1000);
-}
-
-BFP_EXPORT bool BFP_CALLTYPE BfpThread_Yield()
-{
-    return sched_yield() == 0;
-}
-
-struct BfpCritSect
-{
-    pthread_mutex_t mPMutex;
-};
-
-BFP_EXPORT BfpCritSect* BFP_CALLTYPE BfpCritSect_Create()
-{
-    BfpCritSect* critSect = new BfpCritSect();
-
-    pthread_mutexattr_t     attributes;        
-    pthread_mutexattr_init(&attributes);
-    pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_RECURSIVE);
-    pthread_mutex_init(&critSect->mPMutex, &attributes);
-    pthread_mutexattr_destroy(&attributes);
-
-    return critSect;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpCritSect_Release(BfpCritSect* critSect)
-{
-    pthread_mutex_destroy(&critSect->mPMutex);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpCritSect_Enter(BfpCritSect* critSect)
-{
-    pthread_mutex_lock(&critSect->mPMutex);
-}
-
-BFP_EXPORT bool BFP_CALLTYPE BfpCritSect_TryEnter(BfpCritSect* critSect, int waitMS)
-{
-    if (waitMS == -1)
-    {
-        BfpCritSect_Enter(critSect);
-        return true;
-    }
-    else if (waitMS == 0)
-    {
-        return pthread_mutex_trylock(&critSect->mPMutex) == 0;
-    }
-    
-    uint32 start = Beefy::BFTickCount();
-    while ((int)(Beefy::BFTickCount() - start) < waitMS)
-    {
-        if (pthread_mutex_trylock(&critSect->mPMutex) == 0)
-        {                
-            return true;
-        }
-    }
-    return false;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpCritSect_Leave(BfpCritSect* critSect)
-{
-    pthread_mutex_unlock(&critSect->mPMutex);
-}
-
-BFP_EXPORT BfpTLS* BFP_CALLTYPE BfpTLS_Create()
-{
-    pthread_key_t key = 0;
-    pthread_key_create(&key, NULL);
-    return (BfpTLS*)(intptr)key;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpTLS_Release(BfpTLS* tls)
-{
-    pthread_key_delete((pthread_key_t)(intptr)tls);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpTLS_SetValue(BfpTLS* tls, void* value)
-{
-    pthread_setspecific((pthread_key_t)(intptr)tls, value);
-}
-
-BFP_EXPORT void* BFP_CALLTYPE BfpTLS_GetValue(BfpTLS* tls)
-{
-    return pthread_getspecific((pthread_key_t)(intptr)tls);
-}
-
-struct BfpEvent
-{
-    pthread_mutex_t mMutex;
-    pthread_cond_t mCondVariable;
-    bool mSet;
-    bool mManualReset;
-};
-
-BFP_EXPORT BfpEvent* BFP_CALLTYPE BfpEvent_Create(BfpEventFlags flags)
-{
-    BfpEvent* event = new BfpEvent();
-    pthread_mutex_init(&event->mMutex, NULL);
-    pthread_cond_init(&event->mCondVariable, NULL);
-    event->mSet = (flags & (BfpEventFlag_InitiallySet_Auto | BfpEventFlag_InitiallySet_Manual)) != 0;
-    event->mManualReset = (flags & BfpEventFlag_InitiallySet_Manual) != 0;
-    return event;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpEvent_Release(BfpEvent* event)
-{
-    pthread_cond_destroy(&event->mCondVariable);
-    pthread_mutex_destroy(&event->mMutex);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpEvent_Set(BfpEvent* event, bool requireManualReset)
-{
-    pthread_mutex_lock(&event->mMutex);
-    event->mSet = true;
-    if (requireManualReset)
-        event->mManualReset = true;
-    if (event->mManualReset)
-        pthread_cond_broadcast(&event->mCondVariable);
-    else
-        pthread_cond_signal(&event->mCondVariable);
-    pthread_mutex_unlock(&event->mMutex);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpEvent_Reset(BfpEvent* event, BfpEventResult* outResult)
-{
-    event->mSet = false;
-    event->mManualReset = false;
-}
-
-BFP_EXPORT bool BFP_CALLTYPE BfpEvent_WaitFor(BfpEvent* event, int waitMS)
-{
-    int result = pthread_mutex_lock(&event->mMutex);
-    BF_ASSERT(result == 0);
-    while (!event->mSet)
-    {
-        if (waitMS == -1)
-        {
-            pthread_cond_wait(&event->mCondVariable, &event->mMutex);
-        }
-        else
-        {
-            timespec ts;
-            clock_gettime(CLOCK_REALTIME, &ts);
-            ts.tv_sec += waitMS / 1000;
-            ts.tv_nsec += (waitMS % 1000) * 1000000;
-            
-            result = pthread_cond_timedwait(&event->mCondVariable, &event->mMutex, &ts);
-            
-            if (waitMS == (uint32)-1)
-                BF_ASSERT(result == 0);
-            
-            if (result != 0)
-            {
-                // Timeout                
-                pthread_mutex_unlock(&event->mMutex);
-                return false;
-            }
-        }
-    }
-    if (!event->mManualReset)
-        event->mSet = false;        
-    pthread_mutex_unlock(&event->mMutex);
-    return true;
-}
-
-BFP_EXPORT BfpDynLib* BFP_CALLTYPE BfpDynLib_Load(const char* fileName)
-{
-    BfpDynLib* mod = NULL;
-    
-    static const char* prefixes[] = {NULL, "lib"};
-    static const char* suffixes[] = {NULL, ".so", ".dylib"};
-    
-    for (int prefixIdx = 0; prefixIdx < sizeof(prefixes)/sizeof(prefixes[0]); prefixIdx++)
-    {
-        for (int suffixIdx = 0; suffixIdx < sizeof(suffixes)/sizeof(suffixes[0]); suffixIdx++)
-        {
-            const char* prefix = prefixes[prefixIdx];
-            const char* suffix = suffixes[suffixIdx];
-            
-            Beefy::String checkName = fileName;
-            if (prefix != NULL)
-                checkName = Beefy::String(prefix) + checkName;
-            if (suffix != NULL)
-            {
-                int dotPos = checkName.LastIndexOf('.');
-                if (dotPos != -1)
-                    checkName.RemoveToEnd(dotPos);
-                checkName += suffix;
-            }
-            
-            mod = (BfpDynLib*)dlopen(checkName.c_str(), RTLD_LAZY);
-            if (mod != NULL)
-                return mod;
-        }
-    }
-    
-     /*mod = (BfpDynLib*)dlopen("/var/Beef/qt-build/Debug/bin/libIDEHelper.so", RTLD_LAZY);;
-     if (mod == NULL)
-     {
-         printf("Err: %s\n", dlerror());
-         fflush(stdout);
-     }*/
-
-    return NULL;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpDynLib_Release(BfpDynLib* lib)
-{
-    dlclose((void*)lib);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpDynLib_GetFilePath(BfpDynLib* lib, char* outPath, int* inOutPathSize, BfpLibResult* outResult)
-{
-    Beefy::String path;
-
-    
-    Dl_info info;    
-    if (dladdr((void*)lib, &info) == 0)
-    {
-        OUTRESULT(BfpLibResult_UnknownError);
-        return;
-    }
-
-    path = info.dli_fname;
-    TryStringOut(path, outPath, inOutPathSize, (BfpResult*)outResult);
-}
-
-BFP_EXPORT void* BFP_CALLTYPE BfpDynLib_GetProcAddress(BfpDynLib* lib, const char* name)
-{
-    return dlsym((void*)lib, name);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpDirectory_Create(const char* path, BfpFileResult* outResult)
-{
-    if (mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0)
-    {
-        switch (errno)
-        {
-        case EEXIST:
-            OUTRESULT(BfpFileResult_AlreadyExists);
-            break;
-        case ENOENT:
-            OUTRESULT(BfpFileResult_NotFound);
-            break;
-        default:
-            OUTRESULT(BfpFileResult_UnknownError);
-            break;
-        }
-    }
-    else    
-        OUTRESULT(BfpFileResult_Ok);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpDirectory_Rename(const char* oldName, const char* newName, BfpFileResult* outResult)
-{
-    NOT_IMPL;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpDirectory_Delete(const char* path, BfpFileResult* outResult)
-{
-    if (rmdir(path) != 0)
-    {
-        switch (errno)
-        {
-        case ENOENT:
-            OUTRESULT(BfpFileResult_NotFound);
-            break;
-        default:
-            OUTRESULT(BfpFileResult_UnknownError);
-            break;
-        }
-    }
-    else    
-        OUTRESULT(BfpFileResult_Ok);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpDirectory_GetCurrent(char* outPath, int* inOutPathSize, BfpFileResult* outResult)
-{
-    char* str = getcwd(NULL, 0);
-    Beefy::String path = str;
-    free(str);
-    TryStringOut(path, outPath, inOutPathSize, (BfpResult*)outResult);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpDirectory_SetCurrent(const char* path, BfpFileResult* outResult)
-{    
-    if (chdir(path) != 0)
-        OUTRESULT(BfpFileResult_NotFound);  
-    else    
-        OUTRESULT(BfpFileResult_Ok);
-}
-
-BFP_EXPORT bool BFP_CALLTYPE BfpDirectory_Exists(const char* path)
-{
-    struct stat statbuf = {0};
-    int result = stat(path, &statbuf);
-    if (result != 0)
-        return false;
-    return S_ISDIR(statbuf.st_mode);   
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpDirectory_GetSysDirectory(BfpSysDirectoryKind sysDirKind, char* outPath, int* inOutPathLen, BfpFileResult* outResult)
-{
-    String path = "~";
-    TryStringOut(path, outPath, inOutPathLen, (BfpResult*)outResult);
-}
-
-BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_Create(const char* inName, BfpFileCreateKind createKind, BfpFileCreateFlags createFlags, BfpFileAttributes createdFileAttrs, BfpFileResult* outResult)
-{    
-    auto _DoCreate = [&](String& name)
-    {
-        int flags = 0;
-        int mode = 0;
-        int pipePairHandle = -1;
-
-        if ((createFlags & (BfpFileCreateFlag_Read | BfpFileCreateFlag_Write)) == (BfpFileCreateFlag_Read | BfpFileCreateFlag_Write))
-            flags |= O_RDWR;
-        else if ((createFlags & BfpFileCreateFlag_Read) != 0)
-            flags |= O_RDONLY;
-        else if ((createFlags & BfpFileCreateFlag_Write) != 0)
-            flags |= O_WRONLY;
-
-        if ((createFlags & BfpFileCreateFlag_Append) != 0)
-            flags |= O_APPEND;
-        if ((createFlags & BfpFileCreateFlag_Truncate) != 0)
-            flags |= O_TRUNC;
-        if ((createFlags & (BfpFileCreateFlag_NonBlocking | BfpFileCreateFlag_AllowTimeouts)) != 0)
-            flags |= O_NONBLOCK;
-
-        if ((createFlags & BfpFileCreateFlag_Pipe) != 0)
-        {
-            name = "/tmp/" + name;
-
-            if ((createKind == BfpFileCreateKind_CreateAlways) ||
-                (createKind == BfpFileCreateKind_CreateIfNotExists))
-            {
-                for (int pass = 0; pass < 2; pass++)
-                {
-                    int result = mknod(name.c_str(), S_IFIFO | 0666, 0);
-
-                    if (result == 0)
-                        break;
-
-                    int err = errno;
-                    if (err == EEXIST)
-                    {
-                        err = remove(name.c_str());
-                        if (err == 0)
-                            continue;
-                        OUTRESULT(BfpFileResult_AlreadyExists);
-                        return -1;
-                    }
-
-                    OUTRESULT(BfpFileResult_UnknownError);
-                    return -1;
-                }
-            }
-        }
-        else
-        {
-            if (createKind == BfpFileCreateKind_CreateAlways)
-                flags |= O_CREAT;
-            else if (createKind == BfpFileCreateKind_CreateIfNotExists)
-                flags |= O_CREAT | O_EXCL;
-        }
-
-        mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
-
-        int result = open(name.c_str(), flags, mode);
-        //printf("BfpFile_Create %s %d %d %d\n", name.c_str(), result, flags, mode);
-
-        if (result <= 0)
-        {
-            switch (errno)
-            {
-            case EEXIST:
-                OUTRESULT(BfpFileResult_AlreadyExists);
-                break;
-            case ENOENT:
-                OUTRESULT(BfpFileResult_NotFound);
-                break;
-            case EACCES:
-                OUTRESULT(BfpFileResult_AccessError);
-                break;
-            default:
-                OUTRESULT(BfpFileResult_UnknownError);
-                break;
-            }
-            return -1;
-        }
-        return result;
-    };
-
-    BfpFile* bfpFile = NULL;
-
-    int result; 
-    if ((createFlags & BfpFileCreateFlag_Pipe) != 0)
-    {
-        int readHandle;
-        int writeHandle;
-
-        String name = inName;
-        String altName = name + "__";
-
-        bool isCreating = false;
-        if ((createKind == BfpFileCreateKind_CreateAlways) ||
-            (createKind == BfpFileCreateKind_CreateIfNotExists))
-        {
-            readHandle = _DoCreate(name);
-            writeHandle = _DoCreate(altName);
-            isCreating = true;
-        }
-        else
-        {
-            readHandle = _DoCreate(altName);
-            writeHandle = _DoCreate(name);
-        }
-
-        if ((readHandle != -1) && (writeHandle != -1))
-        {
-            OUTRESULT(BfpFileResult_Ok);
-
-            BfpPipeInfo* pipeInfo = new BfpPipeInfo();
-            pipeInfo->mWriteHandle = writeHandle;       
-            if (isCreating)
-                pipeInfo->mPipePath = name;
-            bfpFile = new BfpFile();
-            bfpFile->mHandle = readHandle;          
-            bfpFile->mPipeInfo = pipeInfo;
-        }
-        else
-        {
-            if (readHandle != -1)
-                close(readHandle);
-            if (writeHandle != -1)
-                close(writeHandle);
-
-            return NULL;
-        }
-    }
-    else
-    {
-        String name = inName;
-        int handle = _DoCreate(name);
-        if (handle == -1)
-            return NULL;
-
-        OUTRESULT(BfpFileResult_Ok);
-        bfpFile = new BfpFile();
-        bfpFile->mHandle = handle;
-    }
-
-    OUTRESULT(BfpFileResult_Ok);    
-    if ((createFlags & (BfpFileCreateFlag_NonBlocking | BfpFileCreateFlag_AllowTimeouts)) != 0)
-        bfpFile->mNonBlocking = true;
-    if ((createFlags & BfpFileCreateFlag_AllowTimeouts) != 0)       
-        bfpFile->mAllowTimeout = true;  
-    return bfpFile;
-}
-
-BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_GetStd(BfpFileStdKind kind, BfpFileResult* outResult)
-{
-    int h = -1;
-    switch (kind)
-    {
-    case BfpFileStdKind_StdOut:
-        h = STDOUT_FILENO;
-        break;
-    case BfpFileStdKind_StdError:
-        h = STDERR_FILENO;
-        break;
-    case BfpFileStdKind_StdIn:
-        h = STDIN_FILENO;
-        break;
-    }
-    if (h == -1)
-    {
-        OUTRESULT(BfpFileResult_NotFound);
-        return NULL;
-    }
-
-    BfpFile* bfpFile = new BfpFile();
-    bfpFile->mHandle = h;
-    bfpFile->mIsStd = true;
-
-    return bfpFile;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFile_Release(BfpFile* file)
-{   
-    if ((file->mHandle != -1) && (!file->mIsStd))
-        close(file->mHandle);
-    if (file->mPipeInfo != NULL)
-    {
-        if (file->mPipeInfo->mWriteHandle != -1)
-            close(file->mPipeInfo->mWriteHandle);
-        
-        if (!file->mPipeInfo->mPipePath.IsEmpty())
-        {
-            int worked = remove(file->mPipeInfo->mPipePath.c_str());
-            remove((file->mPipeInfo->mPipePath + "__").c_str());
-            //printf("Removing %s %d\n", file->mPipeInfo->mPipePath.c_str(), worked);
-        }
-    }   
-
-    delete file;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFile_Close(BfpFile* file, BfpFileResult* outResult)
-{
-    if (file->mHandle != -1)
-    {
-        close(file->mHandle);
-        file->mHandle = -1;
-        if (file->mPipeInfo != NULL)
-        {
-            close(file->mPipeInfo->mWriteHandle);
-            file->mPipeInfo->mWriteHandle = -1;
-        }
-
-        OUTRESULT(BfpFileResult_Ok);
-    }
-    else
-        OUTRESULT(BfpFileResult_UnknownError);
-}
-
-BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Write(BfpFile* file, const void* buffer, intptr size, int timeoutMS, BfpFileResult* outResult)
-{   
-    int writeHandle = file->mHandle;
-    if (file->mPipeInfo != NULL)
-        writeHandle = file->mPipeInfo->mWriteHandle;
-
-    intptr writeCount = ::write(writeHandle, buffer, size);
-//  if ((writeCount > 0) && (file->mIsPipe))
-//  {
-//      ::fsync(file->mHandle);
-//  }
-
-    if (writeCount < 0)
-        OUTRESULT(BfpFileResult_UnknownError);
-    else if (writeCount != size)
-        OUTRESULT(BfpFileResult_PartialData);
-    else
-        OUTRESULT(BfpFileResult_Ok);
-    return writeCount;
-}
-
-BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Read(BfpFile* file, void* buffer, intptr size, int timeoutMS, BfpFileResult* outResult)
-{
-    if (file->mNonBlocking)
-    {
-        if (!file->mAllowTimeout)       
-            timeoutMS = -1;     
-        
-        timeval timeout;
-        timeout.tv_sec = 0;
-        timeout.tv_usec = timeoutMS * 1000;
-
-        fd_set readFDSet;
-        FD_ZERO(&readFDSet);
-        FD_SET(file->mHandle, &readFDSet);
-
-        fd_set errorFDSet;
-        FD_ZERO(&errorFDSet);
-        FD_SET(file->mHandle, &errorFDSet);
-
-        if (select(file->mHandle + 1, &readFDSet, NULL, &errorFDSet, (timeoutMS == -1) ? NULL : &timeout) < 0)
-        {
-            OUTRESULT(BfpFileResult_Timeout);
-            return 0;
-        }
-    }
-
-    intptr readCount = ::read(file->mHandle, buffer, size);
-    if (readCount < 0)
-        OUTRESULT(BfpFileResult_UnknownError);
-    else if (readCount != size)
-        OUTRESULT(BfpFileResult_PartialData);
-    else
-        OUTRESULT(BfpFileResult_Ok);
-    return readCount;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFile_Flush(BfpFile* file)
-{
-    ::fsync(file->mHandle);
-}
-
-BFP_EXPORT int64 BFP_CALLTYPE BfpFile_GetFileSize(BfpFile* file)
-{
-    int64 oldPos = (int64)lseek(file->mHandle, 0, SEEK_CUR);
-    int64 size = (int64)lseek(file->mHandle, 0, SEEK_END);
-    lseek(file->mHandle, oldPos, SEEK_SET);
-    return (intptr)size;
-}
-
-BFP_EXPORT int64 BFP_CALLTYPE BfpFile_Seek(BfpFile* file, int64 offset, BfpFileSeekKind seekKind)
-{
-    int whence;
-    if (seekKind == BfpFileSeekKind_Absolute)        
-        whence = SEEK_SET;
-    else if (seekKind == BfpFileSeekKind_Relative)
-        whence = SEEK_CUR;
-    else
-        whence = SEEK_END;
-    return lseek(file->mHandle, offset, whence);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFile_Truncate(BfpFile* file)
-{
-    int64 curPos = (int64)lseek(file->mHandle, 0, SEEK_CUR);
-    if (ftruncate(file->mHandle, curPos) != 0)
-    {
-        //TODO: Report error?
-    }
-}
-
-BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpFile_GetTime_LastWrite(const char* path)
-{
-    struct stat statbuf = {0};
-    int result = stat(path, &statbuf);
-    if (result != 0)
-        return 0;    
-    return statbuf.st_mtime;
-}
-
-BFP_EXPORT BfpFileAttributes BFP_CALLTYPE BfpFile_GetAttributes(const char* path, BfpFileResult* outResult)
-{
-    NOT_IMPL;
-    return (BfpFileAttributes)0;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFile_SetAttributes(const char* path, BfpFileAttributes attribs, BfpFileResult* outResult)
-{
-    NOT_IMPL;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFile_Copy(const char* oldPath, const char* newPath, BfpFileCopyKind copyKind, BfpFileResult* outResult)
-{
-    int fd_to, fd_from;
-    char buf[4096];
-    ssize_t nread;  
-
-    fd_from = open(oldPath, O_RDONLY);
-    if (fd_from < 0)
-    {
-        OUTRESULT(BfpFileResult_NotFound);
-        return;
-    }
-
-    int flags = O_WRONLY | O_CREAT;
-    if (copyKind == BfpFileCopyKind_IfNotExists)
-        flags |= O_EXCL;
-
-    fd_to = open(newPath, flags, 0666);
-    if (fd_to < 0)
-    {
-        if (errno == EEXIST)
-        {
-            OUTRESULT(BfpFileResult_AlreadyExists);
-            goto out_error;
-        }
-
-        OUTRESULT(BfpFileResult_UnknownError);
-        goto out_error;
-    }
-
-    while (nread = read(fd_from, buf, sizeof buf), nread > 0)
-    {
-        char *out_ptr = buf;
-        ssize_t nwritten;
-
-        do {
-            nwritten = write(fd_to, out_ptr, nread);
-
-            if (nwritten >= 0)
-            {
-                nread -= nwritten;
-                out_ptr += nwritten;
-            }
-            else if (errno != EINTR)
-            {
-                OUTRESULT(BfpFileResult_UnknownError);
-                goto out_error;
-            }
-        } while (nread > 0);
-    }
-
-    if (nread == 0)
-    {
-        if (close(fd_to) < 0)
-        {
-            fd_to = -1;
-            OUTRESULT(BfpFileResult_UnknownError);
-            goto out_error;
-        }
-        close(fd_from);
-
-        /* Success! */
-        OUTRESULT(BfpFileResult_Ok);
-        return;
-    }
-
-out_error:
-    close(fd_from);
-    if (fd_to >= 0)
-        close(fd_to);   
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFile_Rename(const char* oldPath, const char* newPath, BfpFileResult* outResult)
-{
-    NOT_IMPL;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFile_Delete(const char* path, BfpFileResult* outResult)
-{
-    if (remove(path) != 0)
-    {
-        switch (errno)
-        {
-        case ENOENT:
-            OUTRESULT(BfpFileResult_NotFound);
-            break;
-        default:
-            OUTRESULT(BfpFileResult_UnknownError);
-            break;
-        }
-    }
-    else    
-        OUTRESULT(BfpFileResult_Ok);
-}
-
-BFP_EXPORT bool BFP_CALLTYPE BfpFile_Exists(const char* path)
-{
-    struct stat statbuf = {0};
-    int result = stat(path, &statbuf);
-    if (result != 0)
-        return false;
-    return !S_ISDIR(statbuf.st_mode);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFile_GetTempPath(char* outPath, int* inOutPathSize, BfpFileResult* outResult)
-{
-    NOT_IMPL;
-}
-
-static const char cHash64bToChar[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
-    'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F',
-    'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
-    'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_' };
-static void HashEncode64(StringImpl& outStr, uint64 val)
-{
-    for (int i = 0; i < 10; i++)
-    {
-        int charIdx = (int)((val >> (i * 6)) & 0x3F) - 1;
-        if (charIdx != -1)
-            outStr.Append(cHash64bToChar[charIdx]);
-    }
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFile_GetTempFileName(char* outName, int* inOutNameSize, BfpFileResult* outResult)
-{
-    static uint32 uniqueIdx = 0;
-    BfpSystem_InterlockedExchangeAdd32(&uniqueIdx, 1);
-
-    Beefy::HashContext ctx;
-    ctx.Mixin(uniqueIdx);
-    ctx.Mixin(getpid());
-    ctx.Mixin(Beefy::BFGetTickCountMicro());
-
-    uint64 hash = ctx.Finish64();
-
-    String str = "/tmp/bftmp_";
-    HashEncode64(str, hash);
-
-    TryStringOut(str, outName, inOutNameSize, (BfpResult*)outResult);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFile_GetFullPath(const char* inPath, char* outPath, int* inOutPathSize, BfpFileResult* outResult)
-{
-    String str;
-
-    if (inPath[0] == '/')
-    {
-        str = inPath;
-    }
-    else
-    {
-        char* cwdPtr = getcwd(NULL, 0);
-        Beefy::String cwdPath = cwdPtr;
-        free(cwdPtr);
-        str = GetAbsPath(inPath, cwdPath);
-    }
-    TryStringOut(str, outPath, inOutPathSize, (BfpResult*)outResult);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFile_GetActualPath(const char* inPath, char* outPath, int* inOutPathSize, BfpFileResult* outResult)
-{
-    NOT_IMPL;
-}
-
-// BfpFindFileData
-
-struct BfpFindFileData
-{
-    BfpFindFileFlags mFlags;    
-    DIR* mDirStruct;
-    Beefy::String mWildcard;
-    Beefy::String mDirPath;
-
-    dirent* mDirEnt;
-    bool mHasStat;
-    struct stat mStat;
-};
-
-BFP_EXPORT BfpFindFileData* BFP_CALLTYPE BfpFindFileData_FindFirstFile(const char* path, BfpFindFileFlags flags, BfpFileResult* outResult)
-{
-    Beefy::String findStr = path;
-    Beefy::String wildcard;
-    
-    int lastSlashPos = std::max((int)findStr.LastIndexOf('/'), (int)findStr.LastIndexOf('\\'));
-    if (lastSlashPos != -1)
-    {
-        wildcard = findStr.Substring(lastSlashPos + 1);
-        findStr = findStr.Substring(0, lastSlashPos);
-    }
-    if (wildcard == "*.*")
-        wildcard = "*";
-    
-    DIR* dir = opendir(findStr.c_str());
-    if (dir == NULL)
-    {
-        OUTRESULT(BfpFileResult_NotFound);
-        return NULL;
-    }
-
-    BfpFindFileData* findData = new BfpFindFileData();
-    findData->mFlags = flags;
-    findData->mDirPath = findStr;
-    findData->mDirStruct = dir;    
-    findData->mWildcard = wildcard;
-    findData->mHasStat = false;
-    findData->mDirEnt = NULL;
-        
-    if (!BfpFindFileData_FindNextFile(findData))
-    {            
-        OUTRESULT(BfpFileResult_NoResults);
-        delete findData;
-        return NULL;
-    }    
-
-    OUTRESULT(BfpFileResult_Ok);
-    return findData;
-}
-
-static void GetStat(BfpFindFileData* findData)
-{
-    if (findData->mHasStat)
-        return;
-
-    Beefy::String filePath = findData->mDirPath + "/" + findData->mDirEnt->d_name;
-    
-    findData->mStat = { 0 };
-    int result = stat(filePath.c_str(), &findData->mStat);
-
-    findData->mHasStat = true;
-}
-
-static bool BfpFindFileData_CheckFilter(BfpFindFileData* findData)
-{
-    bool isDir = false;
-    if (findData->mDirEnt->d_type == DT_DIR)
-        isDir = true;
-    if (findData->mDirEnt->d_type == DT_LNK)
-    {
-        GetStat(findData);
-        isDir = S_ISDIR(findData->mStat.st_mode);
-    }
-
-    if (isDir)
-    {
-        if ((findData->mFlags & BfpFindFileFlag_Directories) == 0)          
-            return false;
-        
-        if ((strcmp(findData->mDirEnt->d_name, ".") == 0) || (strcmp(findData->mDirEnt->d_name, "..") == 0))        
-            return false;        
-    }
-    else
-    {
-        if ((findData->mFlags & BfpFindFileFlag_Files) == 0)
-            return false;
-    }   
-
-    //TODO: Check actual wildcards.
-
-    return true;
-}
-
-BFP_EXPORT bool BFP_CALLTYPE BfpFindFileData_FindNextFile(BfpFindFileData* findData)
-{    
-    while (true)
-    {
-        findData->mHasStat = false;        
-        findData->mDirEnt = readdir(findData->mDirStruct);
-        if (findData->mDirEnt == NULL)
-            return false;
-
-        if (BfpFindFileData_CheckFilter(findData))
-            break;
-    }
-
-    return true;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFindFileData_GetFileName(BfpFindFileData* findData, char* outName, int* inOutNameSize, BfpFileResult* outResult)
-{
-    Beefy::String name = findData->mDirEnt->d_name;
-    TryStringOut(name, outName, inOutNameSize, (BfpResult*)outResult);
-}
-
-BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpFindFileData_GetTime_LastWrite(BfpFindFileData* findData)
-{
-    GetStat(findData);
-    return BfpToTimeStamp(findData->mStat.st_mtimespec);
-}
-
-BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpFindFileData_GetTime_Created(BfpFindFileData* findData)
-{
-    GetStat(findData);
-    return BfpToTimeStamp(findData->mStat.st_ctimespec);
-}
-
-BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpFindFileData_GetTime_Access(BfpFindFileData* findData)
-{
-    GetStat(findData);
-    return BfpToTimeStamp(findData->mStat.st_atimespec);
-}
-
-BFP_EXPORT BfpFileAttributes BFP_CALLTYPE BfpFindFileData_GetFileAttributes(BfpFindFileData* findData)
-{
-    BfpFileAttributes flags = BfpFileAttribute_None;
-    if (S_ISDIR(findData->mStat.st_mode))
-        flags = (BfpFileAttributes)(flags | BfpFileAttribute_Directory);
-    if (S_ISREG(findData->mStat.st_mode))
-        flags = (BfpFileAttributes)(flags | BfpFileAttribute_Normal);
-    else if (!S_ISLNK(findData->mStat.st_mode))
-        flags = (BfpFileAttributes)(flags | BfpFileAttribute_Device);
-    if ((findData->mStat.st_mode & S_IRUSR) == 0)
-        flags = (BfpFileAttributes)(flags | BfpFileAttribute_ReadOnly);
-    return flags;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFindFileData_Release(BfpFindFileData* findData)
-{
-    delete findData;
-}
-
-BFP_EXPORT int BFP_CALLTYPE BfpStack_CaptureBackTrace(int framesToSkip, intptr* outFrames, int wantFrameCount)
-{
-    //
-    return 0;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpOutput_DebugString(const char* str)
-{
-    fputs(str, stdout);
-    fflush(stdout);
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void Beefy::BFFatalError(const StringImpl& message, const StringImpl& file, int line)
-{
-    String error;
-    error += "ERROR: ";
-    error += message;
-    error += " in ";
-    error += file;
-    error += StrFormat(" line %d", line);
-    BfpSystem_FatalError(error.c_str(), "FATAL ERROR");
-}
-
-#if 0
-
-#include "Common.h"
-#include "BFPlatform.h"
-#include <CoreFoundation/CFByteOrder.h>
-#include <mach/mach_time.h>
-#include <sys/stat.h>
-#include <dlfcn.h>
-#include <wchar.h>
-#include <fcntl.h>
-#include <mach/clock.h>
-#include <mach/mach.h>
-#include <time.h>
-#include <dirent.h>
-#include <syslog.h>
-#include "SDL_timer.h"
-
-int gBFPlatformLastError = 0;
-
-#define NOT_IMPL throw "Unimplemented";
-
-inline uint64 get_mach_frequency()
-{
-    uint64 freq = 0;
-    if (freq == 0)
-    {
-        static mach_timebase_info_data_t sTimebaseInfo;
-        if ( sTimebaseInfo.denom == 0)
-            (void) mach_timebase_info(&sTimebaseInfo);
-        
-        freq = (uint64_t)1000000 * (uint64)sTimebaseInfo.denom / (uint64)sTimebaseInfo.numer;
-    }
-    return freq;
-}
-
-uint32 Beefy::BFTickCount()
-{
-    /*uint64 t;
-    t = mach_absolute_time();
-    t /= get_mach_frequency();
-    return (uint32)t;*/
-    return SDL_GetTicks();
-}
-
-int64 Beefy::EndianSwap(int64 val)
-{
-	return CFSwapInt64(val);
-}
-
-bool IsDebuggerPresent()
-{
-    /*   int mib[4];
-     struct kinfo_proc info;
-     size_t size;
-     
-     info.kp_proc.p_flag = 0;
-     mib[0] = CTL_KERN;
-     mib[1] = KERN_PROC;
-     mib[2] = KERN_PROC_PID;
-     mib[3] = getpid();
-     
-     size = sizeof(info);
-     sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
-     
-     return ((info.kp_proc.p_flag & P_TRACED) != 0);*/
-    return true;
-}
-
-void OutputDebugStringA(const char* str)
-{
-    if (IsDebuggerPresent())
-        fputs(str, stdout);
-}
-
-void OutputDebugStringW(const wchar_t* str)
-{
-    if (IsDebuggerPresent())
-    {
-        fputws(str, stdout);
-    }
-}
-
-void* BFTlsGetValue(BFTlsKey tlsKey)
-{
-    return pthread_getspecific(tlsKey);
-}
-
-bool BFTlsSetValue(BFTlsKey tlsKey, void* tlsValue)
-{
-    return pthread_setspecific(tlsKey, tlsValue) == 0;
-}
-
-BFTlsKey BFTlsAlloc()
-{
-    pthread_key_t key = NULL;
-    pthread_key_create(&key, NULL);
-    return key;
-}
-
-bool BFTlsFree(BFTlsKey tlsKey)
-{
-    return pthread_key_delete(tlsKey) == 0;
-}
-
-HANDLE GetModuleHandle(HANDLE h)
-{
-    NOT_IMPL
-    return 0;
-}
-
-uint32 GetModuleFileNameW(uint32 hModule, wchar_t* lpFilename, uint32 length)
-{
-    return (uint32)mbstowcs(lpFilename, gBFArgV[0], length);
-}
-
-uint32 GetModuleFileNameA(uint32 hModule, char* lpFilename, uint32 length)
-{
-    strncpy(lpFilename, gBFArgV[0], length);
-    return (uint32)strlen(lpFilename);
-}
-
-HMODULE LoadLibraryA(const char* fileName)
-{
-    HMODULE mod = NULL;
-    /*(HMODULE)dlopen(fileName, RTLD_LAZY);
-     if (mod == NULL)
-     mod = (HMODULE)dlopen((std::string(fileName) + ".dylib").c_str(), RTLD_LAZY);
-     if (mod == NULL)
-     mod = (HMODULE)dlopen((std::string(fileName) + ".so").c_str(), RTLD_LAZY);*/
-    
-    static const char* prefixes[] = {NULL, "lib"};
-    static const char* suffixes[] = {NULL, ".so", ".dylib"};
-    
-    for (int prefixIdx = 0; prefixIdx < sizeof(prefixes)/sizeof(prefixes[0]); prefixIdx++)
-    {
-        for (int suffixIdx = 0; suffixIdx < sizeof(suffixes)/sizeof(suffixes[0]); suffixIdx++)
-        {
-            const char* prefix = prefixes[prefixIdx];
-            const char* suffix = suffixes[suffixIdx];
-            
-            std::string checkName = fileName;
-            if (prefix != NULL)
-                checkName = std::string(prefix) + checkName;
-            if (suffix != NULL)
-                checkName += suffix;
-            
-            mod = (HMODULE)dlopen(checkName.c_str(), RTLD_LAZY);
-            if (mod != NULL)
-                return mod;
-        }
-    }
-    
-    return NULL;
-}
-
-BFLibProcAddr GetProcAddress(HMODULE mod, const char* name)
-{
-    return (BFLibProcAddr)dlsym(mod, name);
-}
-
-bool CreateDirectoryW(const wchar_t* str, void* securityAttributes)
-{NOT_IMPL
-    return false;
-}
-
-bool RemoveDirectoryW(const wchar_t* str)
-{NOT_IMPL
-    return false;
-}
-
-int GetLastError()
-{
-    return gBFPlatformLastError;
-}
-
-void BFSetLastError(int error)
-{
-    gBFPlatformLastError = error;
-}
-
-int* GetStdHandle(int32 handleId)
-{
-    if (handleId == STD_INPUT_HANDLE)
-        return (int*)STDIN_FILENO;
-    if (handleId == STD_OUTPUT_HANDLE)
-        return (int*)STDOUT_FILENO;
-    return (int*)STDERR_FILENO;
-}
-
-#ifdef USING_PTHREAD
-intptr GetCurrentThreadId()
-{
-    return (intptr)pthread_self();
-}
-
-HANDLE GetCurrentThread()
-{
-    return pthread_self();
-}
-
-bool SetThreadPriority(HANDLE hThread, int nPriority)
-{
-    return false;
-}
-
-void* CreateThread(void* threadAttributes, int32 stackSize, BFThreadStartProc threadStartProc, void* param, uint32 flags, intptr* threadId)
-{
-    BF_ASSERT(sizeof(pthread_t) <= sizeof(void*));
-    pthread_t thread;
-    pthread_attr_t params;
-    pthread_attr_init(&params);
-    pthread_attr_setstacksize(&params, stackSize);
-    //pthread_attr_setdetachstate(&params,PTHREAD_CREATE_DETACHED);
-    pthread_create(threadId, &params, (void*(*)(void *))threadStartProc, param);
-    thread = (pthread_t)*threadId;
-    pthread_attr_destroy(&params);
-    
-    //printf("CreateThread: %08X\n", (intptr)thread);
-    
-    return (void*)thread;
-}
-
-void ResumeThread(HANDLE thread)
-{NOT_IMPL
-    thread_suspend(thread);
-}
-
-uint32 SuspendThread(HANDLE thread)
-{NOT_IMPL
-}
-#else
-BF_THREADID GetCurrentThreadId()
-{
-    return pthread_self();
-}
-
-HANDLE GetCurrentThread()
-{
-    return (HANDLE)(intptr)mach_thread_self();
-}
-
-static int gThreadPriorityDefaultValue = -1;
-
-int GetThreadPriority(HANDLE hThread)
-{
-    mach_msg_type_number_t count = 1;
-    boolean_t getDefault = false;
-    thread_precedence_policy_data_t precedence;
-    precedence.importance = -1;
-    kern_return_t result = thread_policy_get((thread_act_t)(intptr)hThread,
-                                             THREAD_PRECEDENCE_POLICY,
-                                             (thread_policy_t)&precedence,
-                                             &count,
-                                             &getDefault);
-    return precedence.importance - gThreadPriorityDefaultValue;
-}
-
-bool SetThreadPriority(HANDLE hThread, int nPriority)
-{
-    /*thread_precedence_policy_data_t precedence;
-    precedence.importance = 0;
-    
-    if (gThreadPriorityDefaultValue == -1)
-    {
-        mach_msg_type_number_t count = 1;
-        boolean_t getDefault = false;
-        kern_return_t result = thread_policy_get((thread_act_t)(intptr)hThread,
-                                                 THREAD_PRECEDENCE_POLICY,
-                                                 (thread_policy_t)&precedence,
-                                                 &count,
-                                                 &getDefault);
-        gThreadPriorityDefaultValue = precedence.importance;
-    }
-    precedence.importance = gThreadPriorityDefaultValue + nPriority * 16;
-    kern_return_t result = thread_policy_set((thread_act_t)(intptr)hThread,
-                               THREAD_PRECEDENCE_POLICY,
-                               (thread_policy_t)&precedence,
-                               THREAD_PRECEDENCE_POLICY_COUNT);
-    return result == 0;*/
-    
-    int policy = 0;
-    struct sched_param sched;
-    sched.sched_priority = 0;
-    pthread_getschedparam(pthread_self(), &policy, &sched);
-    sched.sched_priority = std::min(nPriority * 16 + 47, 62);
-    //int retVal = pthread_setschedparam(pthread_self(), policy, &sched);
-    
-    return true;
-}
-
-HANDLE OpenThread(int desiredAccess, bool inheritHandle, BF_THREADID threadId)
-{
-    return (HANDLE)(intptr)mach_thread_self();
-}
-
-void* CreateThread(void* threadAttributes, int32 stackSize, BFThreadStartProc threadStartProc, void* param, uint32 flags, BF_THREADID* threadId)
-{
-    BF_ASSERT(sizeof(pthread_t) <= sizeof(void*));
-    pthread_t thread;
-    pthread_attr_t params;
-    pthread_attr_init(&params);
-    pthread_attr_setstacksize(&params, stackSize);
-    //pthread_attr_setdetachstate(&params,PTHREAD_CREATE_DETACHED);
-    pthread_create(&thread, &params, (void*(*)(void *))threadStartProc, param);
-    pthread_attr_destroy(&params);
-    *threadId = thread;
-    
-    //printf("CreateThread: %08X\n", (int32)(intptr)thread);
-    
-    return (void*)thread;
-}
-
-uint32 SuspendThread(HANDLE thread)
-{
-    kern_return_t kernResult = thread_suspend((thread_act_t)(intptr)thread);
-    BF_ASSERT(kernResult == KERN_SUCCESS);
-    return 0;
-}
-
-uint32 ResumeThread(HANDLE thread)
-{
-    kern_return_t kernResult = thread_resume((thread_act_t)(intptr)thread);
-    BF_ASSERT(kernResult == KERN_SUCCESS);
-    return 0;
-}
-
-
-#endif
-
-void Sleep(int32 ms)
-{
-    usleep(ms * 1000);
-}
-
-struct BFFindFileData
-{
-    DIR* mDirStruct;
-    std::string mWildcard;
-};
-
-HANDLE FindFirstFileW(wchar_t* lpFileName, WIN32_FIND_DATAW* lpFindFileData)
-{
-    std::string findStr = Beefy::UTF8Encode(lpFileName);
-    std::string wildcard;
-    
-    int lastSlashPos = std::max((int)findStr.rfind('/'), (int)findStr.rfind('\\'));
-    if (lastSlashPos != -1)
-    {
-        wildcard = findStr.substr(lastSlashPos + 1);
-        findStr = findStr.substr(0, lastSlashPos);
-    }
-    
-    DIR* dir = opendir(findStr.c_str());
-    if (dir == NULL)
-    {
-        BFSetLastError(ERROR_FILE_NOT_FOUND);
-        return INVALID_HANDLE_VALUE;
-    }
-    
-    BFFindFileData* bfFindFileData = new BFFindFileData();
-    bfFindFileData->mDirStruct = dir;
-    bfFindFileData->mWildcard = wildcard;
-    
-    if (!FindNextFileW((HANDLE)bfFindFileData, lpFindFileData))
-    {
-        FindClose((HANDLE)bfFindFileData);
-        BFSetLastError(ERROR_NO_MORE_FILES);
-        return INVALID_HANDLE_VALUE;
-    }
-    
-    return (HANDLE)bfFindFileData;
-}
-
-void BFSystemTimeToFileTime(time_t timeT, long nsec, FILETIME* lpFileTime)
-{
-    uint64 ft64 = (timeT * 10000000ULL) + (nsec / 100);
-    lpFileTime->dwHighDateTime = (uint32)(ft64 >> 32);
-    lpFileTime->dwLowDateTime = (uint32)ft64;
-}
-
-void BFSystemTimeToFileTime(timespec& timeSpec, FILETIME* lpFileTime)
-{
-    uint64 ft64 = (timeSpec.tv_nsec * 10000000ULL) + (timeSpec.tv_nsec / 100);
-    lpFileTime->dwHighDateTime = (uint32)(ft64 >> 32);
-    lpFileTime->dwLowDateTime = (uint32)ft64;
-}
-
-bool FindNextFileW(HANDLE hFindFile, WIN32_FIND_DATAW* lpFindFileData)
-{
-    memset(lpFindFileData, 0, sizeof(WIN32_FIND_DATAW));
-    
-    BFFindFileData* bfFindFileData = (BFFindFileData*)hFindFile;
-    dirent* ent = NULL;
-    while (true)
-    {
-        ent = readdir(bfFindFileData->mDirStruct);
-        if (ent == NULL)
-            return false;
-        
-        if (bfFindFileData->mWildcard == "*")
-            break;
-        std::string fileName = ent->d_name;
-        if (bfFindFileData->mWildcard[0] == '*')
-        {
-            if (fileName.length() < bfFindFileData->mWildcard.length() - 1)
-                continue;
-            
-            std::string checkFileName = fileName.substr(fileName.length() - bfFindFileData->mWildcard.length() + 1);
-            if (checkFileName == bfFindFileData->mWildcard.substr(1))
-                break;
-        }
-    }
-    mbstowcs(lpFindFileData->cFileName, ent->d_name, 1024);
-    
-    struct stat statbuf = {0};
-    int result = stat(ent->d_name, &statbuf);
-    if (result == -1)
-        return -1;
-    if (S_ISDIR(statbuf.st_mode))
-        lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
-    if (S_ISREG(statbuf.st_mode))
-        lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_NORMAL;
-    else if (!S_ISLNK(statbuf.st_mode))
-        lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DEVICE;
-    if ((statbuf.st_mode & S_IRUSR) == 0)
-        lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
-    
-    lpFindFileData->nFileSizeLow = (int32)statbuf.st_size;
-    lpFindFileData->nFileSizeHigh = (int32)(statbuf.st_size >> 32);
-    BFSystemTimeToFileTime(statbuf.st_ctimespec, &lpFindFileData->ftCreationTime);
-    BFSystemTimeToFileTime(statbuf.st_atimespec, &lpFindFileData->ftLastAccessTime);
-    BFSystemTimeToFileTime(statbuf.st_mtimespec, &lpFindFileData->ftLastWriteTime);
-    
-    return true;
-}
-
-bool FindClose(HANDLE hFindFile)
-{
-    BFFindFileData* bfFindFileData = (BFFindFileData*)hFindFile;
-    closedir(bfFindFileData->mDirStruct);
-    delete bfFindFileData;
-    return true;
-}
-
-uint32 GetCurrentDirectoryW(uint32 nBufferLength, wchar_t* lpBuffer)
-{
-    char str[2048];
-    getcwd(str, 2048);
-    return (uint32)mbstowcs(lpBuffer, str, nBufferLength);
-}
-
-bool SetCurrentDirectoryW(wchar_t* lpBuffer)
-{
-    if (chdir(Beefy::UTF8Encode(lpBuffer).c_str()) == 0)
-        return true;
-    BFSetLastError(ERROR_FILE_NOT_FOUND);
-    return false;
-}
-
-bool CopyFileW(wchar_t* lpExistingFileName, wchar_t* lpNewFileName, bool allowOverwrite)
-{NOT_IMPL
-    return true;
-}
-
-bool MoveFileW(wchar_t* lpExistingFileName, wchar_t* lpNewFileName)
-{NOT_IMPL
-    return true;
-}
-
-bool DeleteFileW(wchar_t* lpFileName)
-{NOT_IMPL
-    return true;
-}
-
-bool ReplaceFileW(wchar_t* lpReplacedFileName, wchar_t* lpReplacementFileName, wchar_t* lpBackupFileName,
-                  uint32 dwReplaceFlags, void* lpExclude, void* lpReserved)
-{NOT_IMPL
-    return true;
-}
-
-bool SetFileAttributesW(wchar_t* lpFileName, uint32 dwFileAttributes)
-{NOT_IMPL
-    return true;
-}
-
-int32 GetFileType(HANDLE fileHandle)
-{
-    if (isatty((int)(intptr)fileHandle))
-        return FILE_TYPE_CHAR;
-    return FILE_TYPE_DISK;
-}
-
-bool GetFileAttributesExW(wchar_t* lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, void* lpFileInformation)
-{NOT_IMPL
-    return 0;
-}
-
-HANDLE CreateFileW(wchar_t* lpFileName, uint32 dwDesiredAccess, uint32 dwShareMode, SECURITY_ATTRIBUTES* lpSecurityAttributes,
-                   uint32 dwCreationDisposition, uint32 dwFlagsAndAttributes, HANDLE hTemplateFile)
-{
-    std::string fileName = Beefy::UTF8Encode(lpFileName);
-    int flags = 0;
-    int mode = 0;
-    
-    if ((dwDesiredAccess & (GENERIC_READ | GENERIC_WRITE)) == (GENERIC_READ | GENERIC_WRITE))
-        flags |= O_RDWR;
-    else if (dwDesiredAccess & GENERIC_READ)
-        flags |= O_RDONLY;
-    else if (dwDesiredAccess & GENERIC_WRITE)
-        flags |= O_WRONLY;
-    
-    if (dwCreationDisposition == OPEN_ALWAYS)
-        flags |= O_CREAT;
-    else if (dwCreationDisposition == CREATE_ALWAYS)
-        flags |= O_CREAT | O_TRUNC;
-    else if (dwCreationDisposition == CREATE_NEW)
-        flags |= O_CREAT | O_EXCL;
-    else if (dwCreationDisposition == TRUNCATE_EXISTING)
-        flags |= O_TRUNC;
-    
-    mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
-    
-    return (HANDLE)(intptr)open(fileName.c_str(), flags, mode);
-}
-
-bool CreatePipe(HANDLE* hReadPipe, HANDLE* hWritePipe, SECURITY_ATTRIBUTES* lpPipeAttributes, uint32 nSize)
-{NOT_IMPL
-    return true;
-}
-
-bool WriteFile(HANDLE hFile, void* lpBuffer, uint32 nNumberOfBytesToWrite, uint32* lpNumberOfBytesWritten, OVERLAPPED* lpOverlapped)
-{
-#ifdef BF_PLATFORM_IOS
-    int logType = -1;
-    if (hFile == (int*)STDOUT_FILENO)
-        logType = LOG_WARNING;
-    else if (hFile == (int*)STDERR_FILENO)
-        logType = LOG_ERR;
-    
-    if (logType != -1)
-    {
-        static std::string strOut;
-        strOut.resize(nNumberOfBytesToWrite);
-        memcpy(&strOut[0], lpBuffer, nNumberOfBytesToWrite);
-        if ((strOut[0] != '\r') && (strOut[0] != '\n'))
-            syslog(LOG_WARNING, "%s", strOut.c_str());
-    }
-#endif
-    
-    int writeCount = (int)::write((int)(intptr)hFile, lpBuffer, nNumberOfBytesToWrite);
-    if (writeCount == -1)
-    {
-        //TODO: set gBFPlatformLastError
-        lpNumberOfBytesWritten = 0;
-        return false;
-    }
-    
-    *lpNumberOfBytesWritten = (uint32)writeCount;
-    return true;
-}
-
-static bool doSleep = false;
-
-bool ReadFile(HANDLE hFile, void* lpBuffer, uint32 nNumberOfBytesToRead, uint32* lpNumberOfBytesRead, OVERLAPPED* lpOverlapped)
-{
-    int readCount = (int)::read((int)(intptr)hFile, lpBuffer, nNumberOfBytesToRead);
-    
-    if (doSleep)
-        Sleep(1000);
-    
-    if (readCount == -1)
-    {
-        //TODO: set gBFPlatformLastError
-        lpNumberOfBytesRead = 0;
-        return false;
-    }
-    
-    *lpNumberOfBytesRead = (uint32)readCount;
-    return true;
-}
-
-bool CloseHandle_File(HANDLE handle)
-{
-    return close((int)(intptr)handle) == 0;
-}
-
-bool FlushFileBuffers(HANDLE handle)
-{
-    return fsync((int)(intptr)handle) == 0;
-}
-
-int32 SetFilePointer(HANDLE handle, int32 distanceToMove, int32* distanceToMoveHigh, uint32 moveMethod)
-{
-    return (int32)lseek((int)(intptr)handle, distanceToMove, (moveMethod == FILE_BEGIN) ? SEEK_SET : (moveMethod == FILE_CURRENT) ? SEEK_CUR : SEEK_END);
-}
-
-int32 GetFileSize(HANDLE hFile, uint32* fileSizeHigh)
-{
-    *fileSizeHigh = 0;
-    int32 oldPos = (int32)lseek((int)(intptr)hFile, 0, SEEK_CUR);
-    int32 size = (int32)lseek((int)(intptr)hFile, 0, SEEK_END);
-    lseek((int)(intptr)hFile, oldPos, SEEK_SET);
-    return size;
-}
-
-bool SetEndOfFile(HANDLE hFile)
-{
-    int32 curPos = (int32)lseek((int)(intptr)hFile, 0, SEEK_CUR);
-    ftruncate((int)(intptr)hFile, curPos);
-    return true;
-}
-
-bool SetFileTime(HANDLE hFile, const FILETIME* lpCreationTime, const FILETIME* lpLastAccessTime, const FILETIME* lpLastWriteTime)
-{NOT_IMPL
-    return true;
-}
-
-bool LockFile(HANDLE hFile, uint32 dwFileOffsetLow, uint32 dwFileOffsetHigh, uint32 nNumberOfBytesToLockLow, uint32 nNumberOfBytesToLockHigh)
-{NOT_IMPL
-    return true;
-}
-
-bool UnlockFile(HANDLE hFile, uint32 dwFileOffsetLow, uint32 dwFileOffsetHigh, uint32 nNumberOfBytesToUnlockLow, uint32 nNumberOfBytesToUnlockHigh)
-{NOT_IMPL
-    return true;
-}
-
-bool DuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle, HANDLE* lpTargetHandle,
-                     uint32 dwDesiredAccess, bool bInheritHandle, uint32 dwOptions)
-{NOT_IMPL
-    return true;
-}
-
-int32 GetTempPath(int32 bufferLen, wchar_t* str)
-{NOT_IMPL
-    return 0;
-}
-
-HANDLE CreateEventW(SECURITY_ATTRIBUTES* lpEventAttributes, bool bManualReset, bool bInitialState, wchar_t* lpName)
-{
-    BF_ASSERT(lpName == NULL);
-    Beefy::SyncEvent* syncEvent = new Beefy::SyncEvent(bManualReset, bInitialState);
-    return syncEvent;
-}
-
-HANDLE OpenEventW(uint32 dwDesiredAccess, bool bInheritHandle, wchar_t* lpName)
-{NOT_IMPL
-    return 0;
-}
-
-bool SetEvent(HANDLE handle)
-{
-    ((Beefy::SyncEvent*)handle)->Set();
-    return true;
-}
-
-bool ResetEvent(HANDLE handle)
-{
-    ((Beefy::SyncEvent*)handle)->Reset();
-    return true;
-}
-
-bool CloseHandle_Event(HANDLE handle)
-{
-    delete (Beefy::SyncEvent*)handle;
-    return true;
-}
-
-
-int WaitForSingleObject(HANDLE obj, int waitMs)
-{
-    Beefy::SyncEvent* syncEvent = (Beefy::SyncEvent*)obj;
-    bool worked = syncEvent->WaitFor(waitMs);
-    return worked ? WAIT_OBJECT_0 : -1;
-}
-
-int WaitForSingleObject_Thread(HANDLE obj, int waitMs)
-{
-    BF_ASSERT(waitMs == -1);
-    //printf("WaitForSingleObject_Thread: %08X\n", (int32)(intptr)obj);
-    int result = pthread_join((pthread_t)obj, NULL);
-    BF_ASSERT(result == 0);
-    return WAIT_OBJECT_0;
-}
-
-HANDLE CreateMutexW(SECURITY_ATTRIBUTES* lpMutexAttributes, bool bInitialOwner, wchar_t* lpName)
-{NOT_IMPL
-    return 0;
-}
-
-HANDLE OpenMutexW(uint32 dwDesiredAccess, bool bInheritHandle, wchar_t* lpName)
-{NOT_IMPL
-    return 0;
-}
-
-bool ReleaseMutex(HANDLE mutex)
-{NOT_IMPL
-    return true;
-}
-
-uint32 FormatMessageW(uint32 dwFlags, void* lpSource, uint32 dwMessageId, uint32 dwLanguageId, wchar_t* lpBuffer, uint32 nSize, va_list* Arguments)
-{NOT_IMPL
-    return 0;
-}
-
-int32 WSAGetLastError()
-{NOT_IMPL
-    return 0;
-}
-
-void GetExitCodeProcess(HANDLE handle, DWORD* code)
-{NOT_IMPL
-}
-
-bool CloseHandle_Thread(HANDLE handle)
-{NOT_IMPL
-    return true;
-}
-
-bool CloseHandle_Process(HANDLE handle)
-{NOT_IMPL
-    return true;
-}
-
-bool GetProcessTimes(HANDLE handle, FILETIME* createTime, FILETIME* exitTime, FILETIME* kernelTime, FILETIME* userTime)
-{NOT_IMPL
-    return true;
-}
-
-bool GetProcessWorkingSetSize(HANDLE handle, size_t* curMin, size_t* curMax)
-{NOT_IMPL
-    return true;
-}
-
-bool SetProcessWorkingSetSize(HANDLE handle, size_t curMin, size_t curMax)
-{NOT_IMPL
-    return true;
-}
-
-bool EnumProcessModules(HANDLE handle, HMODULE* mods, DWORD cb, DWORD* needed)
-{NOT_IMPL
-    return true;
-}
-
-HANDLE OpenProcess(DWORD desiredAccess, bool inheritHandle, DWORD pid)
-{NOT_IMPL
-    return 0;
-}
-
-bool GetModuleBaseNameW(HANDLE handle, HMODULE mod, wchar_t* modname, int maxLen)
-{NOT_IMPL
-    return true;
-}
-
-bool GetModuleFileNameExW(HANDLE handle, HMODULE mod, wchar_t* modname, int maxLen)
-{NOT_IMPL
-    return true;
-}
-
-bool GetModuleInformation(HANDLE handle, HMODULE mod, MODULEINFO* modinfo, int modInfoSize)
-{NOT_IMPL
-    return true;
-}
-
-int GetPriorityClass(HANDLE handle)
-{NOT_IMPL
-    return 0;
-}
-
-bool SetPriorityClass(HANDLE handle, int priClass)
-{NOT_IMPL
-    return true;
-}
-
-bool TerminateProcess(HANDLE handle, int termCode)
-{NOT_IMPL
-    return true;
-}
-
-bool WaitForInputIdle(HANDLE handle, int ms)
-{NOT_IMPL
-    return true;
-}
-
-int32 GetCurrentProcessId()
-{NOT_IMPL
-    return 0;
-}
-
-HANDLE GetCurrentProcess()
-{NOT_IMPL
-    return 0;
-}
-
-bool GetDiskFreeSpaceExW(wchar_t* pathNameStr, ULARGE_INTEGER* wapi_free_bytes_avail, ULARGE_INTEGER*wapi_total_number_of_bytes, ULARGE_INTEGER* wapi_total_number_of_free_bytes)
-{NOT_IMPL
-    return true;
-}
-
-uint32 GetDriveTypeW(wchar_t* driveName)
-{NOT_IMPL
-    return 0;
-}
-
-bool GetVolumeInformationW(wchar_t* lpRootPathName, wchar_t* lpVolumeNameBuffer, uint32 nVolumeNameSize, uint32* lpVolumeSerialNumber, uint32* lpMaximumComponentLength, uint32* lpFileSystemFlags, wchar_t* lpFileSystemNameBuffer, uint32 nFileSystemNameSize)
-{NOT_IMPL
-    return true;
-}
-
-uint64 BFClockGetTime()
-{
-    static bool initialized = false;
-    static clock_serv_t cclock;
-    if (!initialized)
-    {
-        host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
-        initialized = true;
-    }
-    
-    time_t rawtime;
-    struct tm timeinfo;
-    time(&rawtime);
-    timeinfo = *localtime(&rawtime);
-    
-    mach_timespec_t mts;
-    clock_get_time(cclock, &mts);
-    //mach_port_deallocate(mach_task_self(), cclock);
-    //if (timeinfo.tm_isdst)
-        //mts.tv_sec += 60 * 60;
-    return ((mts.tv_sec /*+ timeinfo.tm_gmtoff*/) * 10000000ULL) + (mts.tv_nsec / 100);
-}
-
-DWORD GetTimeZoneInformation(TIME_ZONE_INFORMATION* lpTimeZoneInformation)
-{
-    std::wstring tzName0 = Beefy::UTF8Decode(tzname[0]);
-    std::wstring tzName1 = Beefy::UTF8Decode(tzname[1]);
-    
-    bool isDST = false;
-    
-    time_t timeNow;
-    time(&timeNow);
-    tm tmNow = *gmtime(&timeNow);
-    isDST = tmNow.tm_isdst;
-    
-    struct tm checkTM;
-    memset(&checkTM, 0, sizeof(tm));
-    checkTM.tm_mday = 1;
-    checkTM.tm_year = tmNow.tm_year;
-    time_t checkTime = mktime(&checkTM);
-    
-    time_t lastOffset = 0;
-    time_t minOffset = 0;
-    time_t maxOffset = 0;
-    
-    for (int pass = 0; pass < 2; pass++)
-    {
-        int searchDir = 60*60*24;
-        int thresholdCount = 0;
-        
-        while (true)
-        {
-            checkTime += searchDir;
-            
-            tm checkTM = *gmtime(&checkTime);
-            
-            if (checkTM.tm_year != tmNow.tm_year)
-                break; // No DST
-            
-            mktime(&checkTM);
-            
-            time_t offset = checkTM.tm_gmtoff;
-            if (lastOffset != offset)
-            {
-                if (thresholdCount == 0)
-                {
-                    minOffset = offset;
-                    maxOffset = offset;
-                }
-                else if (thresholdCount == 3)
-                {
-                    SYSTEMTIME* sysTimeP = (offset == minOffset) ?
-                    &lpTimeZoneInformation->StandardDate :
-                    &lpTimeZoneInformation->DaylightDate;
-                    
-                    if (offset == minOffset)
-                        tzName0 = Beefy::UTF8Decode(checkTM.tm_zone);
-                    else
-                        tzName1 = Beefy::UTF8Decode(checkTM.tm_zone);
-                    
-                    sysTimeP->wDay = 0;
-                    sysTimeP->wDayOfWeek = 0;
-                    sysTimeP->wYear = checkTM.tm_year + 1900;
-                    sysTimeP->wMonth = checkTM.tm_mon;
-                    sysTimeP->wDay = checkTM.tm_mday + 1;
-                    sysTimeP->wHour = checkTM.tm_hour;
-                    sysTimeP->wMinute = checkTM.tm_min;
-                    sysTimeP->wSecond = checkTM.tm_sec;
-                    sysTimeP->wMilliseconds = 0;
-                    
-                    break;
-                }
-                else
-                {
-                    if (thresholdCount == 1)
-                        searchDir /= -24;
-                    else
-                        searchDir /= -60;
-                    minOffset = std::min(minOffset, offset);
-                    maxOffset = std::max(maxOffset, offset);
-                }
-                thresholdCount++;
-                lastOffset = offset;
-            }
-        }
-    }
-	
-    wcsncpy(lpTimeZoneInformation->StandardName, tzName0.c_str(), 32);
-    wcsncpy(lpTimeZoneInformation->DaylightName, tzName1.c_str(), 32);
-    
-    lpTimeZoneInformation->DaylightBias = (int32)maxOffset;
-    lpTimeZoneInformation->StandardBias = (int32)minOffset;
-    
-    if (minOffset == maxOffset)
-        return 0;
-    return isDST ? 2 : 1;
-}
-
-bool SystemTimeToFileTime(const SYSTEMTIME* lpSystemTime, FILETIME* lpFileTime)
-{
-    tm checkTM;
-    checkTM.tm_year = lpSystemTime->wYear - 1900;
-    checkTM.tm_mon = lpSystemTime->wMonth;
-    checkTM.tm_mday = lpSystemTime->wDay - 1;
-    checkTM.tm_hour = lpSystemTime->wHour;
-    checkTM.tm_min = lpSystemTime->wMinute;
-    checkTM.tm_sec = lpSystemTime->wSecond;
-    
-    time_t timeT = mktime(&checkTM);
-    
-    uint64 ft64 = timeT * 10000000ULL;
-    
-    lpFileTime->dwHighDateTime = (uint32)(ft64 >> 32);
-    lpFileTime->dwLowDateTime = (uint32)ft64;
-    return true;
-}
-
-uint64 Beefy::BFGetTickCountMicro()
-{
-    static bool initialized = false;
-    static clock_serv_t cclock;
-    if (!initialized)
-    {
-        host_get_clock_service(mach_host_self(), REALTIME_CLOCK, &cclock);
-        initialized = true;
-    }
-    
-    mach_timespec_t mts;
-    clock_get_time(cclock, &mts);
-    return (mts.tv_sec * 1000000ULL) + (mts.tv_nsec / 1000);
-}
-
-uint64 Beefy::BFGetTickCountMicroFast()
-{
-    return BFGetTickCountMicro();
-}
-
-#if (defined __IPHONEOS__) && !TARGET_IPHONE_SIMULATOR
-
-void BFGetThreadRegisters(HANDLE threadHandle, intptr* stackPtr, intptr* dataPtr)
-{
-    mach_msg_type_number_t thread_state_count = ARM_UNIFIED_THREAD_STATE_COUNT;
-    arm_unified_thread_state_t unifiedState;
-    kern_return_t kernResult = thread_get_state((thread_act_t)(intptr)threadHandle, ARM_UNIFIED_THREAD_STATE, (natural_t*)&unifiedState.ts_32, &thread_state_count);
-
-#ifdef BF32
-    arm_thread_state32_t state = unifiedState.ts_32;
-#else
-    zzz SHOULDN'T GET HERE
-    arm_thread_state64_t state = unifiedState.ts_64;
-#endif
-      
-    BF_ASSERT(kernResult == KERN_SUCCESS);
-    
-    *stackPtr = (intptr)state.__sp;
-    
-    if (dataPtr != NULL)
-	{
-#ifdef BF32
-		intptr* curPtr = dataPtr;
-		*(curPtr++) = (intptr)state.__r[0];
-   		*(curPtr++) = (intptr)state.__r[1];
-		*(curPtr++) = (intptr)state.__r[2];
-   		*(curPtr++) = (intptr)state.__r[3];
-		*(curPtr++) = (intptr)state.__r[4];
-   		*(curPtr++) = (intptr)state.__r[5];
-		*(curPtr++) = (intptr)state.__r[6];
-   		*(curPtr++) = (intptr)state.__r[7];
-		*(curPtr++) = (intptr)state.__r[8];
-   		*(curPtr++) = (intptr)state.__r[9];
-		*(curPtr++) = (intptr)state.__r[10];
-   		*(curPtr++) = (intptr)state.__r[11];
-		*(curPtr++) = (intptr)state.__r[12];
-   		*(curPtr++) = (intptr)state.__lr;
-      	*(curPtr++) = (intptr)state.__cpsr;
-#else
-		intptr* curPtr = dataPtr;
-#endif
-        int count = (int)(curPtr - dataPtr);
-		BF_ASSERT(count == BF_REGISTER_COUNT);
-	}
-}
-
-#else
-
-void BFGetThreadRegisters(HANDLE threadHandle, intptr* stackPtr, intptr* dataPtr)
-{
-#ifdef BF32
-    mach_msg_type_number_t thread_state_count = x86_THREAD_STATE32_COUNT;
-    x86_thread_state32_t state;
-    kern_return_t kernResult = thread_get_state((thread_act_t)threadHandle, x86_THREAD_STATE32, (natural_t*)&state, &thread_state_count);
-#else
-    mach_msg_type_number_t thread_state_count = x86_THREAD_STATE64_COUNT;
-    x86_thread_state64_t state;
-    kern_return_t kernResult = thread_get_state((thread_act_t)threadHandle, x86_THREAD_STATE64, (natural_t*)&state, &thread_state_count);
-#endif
-    BF_ASSERT(kernResult == KERN_SUCCESS);
-    
-    *stackPtr = (intptr)state.__esp;
-    
-    if (dataPtr != NULL)
-	{
-#ifdef BF32
-		intptr* curPtr = dataPtr;
-		*(curPtr++) = (intptr)state.__eax;
-		*(curPtr++) = (intptr)state.__ebx;
-		*(curPtr++) = (intptr)state.__ecx;
-		*(curPtr++) = (intptr)state.__edx;
-		*(curPtr++) = (intptr)state.__esi;
-		*(curPtr++) = (intptr)state.__edi;
-        *(curPtr++) = (intptr)state.__ebp;
-#else
-		intptr* curPtr = dataPtr;
-		*(curPtr++) = (intptr)ctx.Rax;
-		*(curPtr++) = (intptr)ctx.Rbx;
-		*(curPtr++) = (intptr)ctx.Rcx;
-		*(curPtr++) = (intptr)ctx.Rdx;
-		*(curPtr++) = (intptr)ctx.Rsi;
-		*(curPtr++) = (intptr)ctx.Rdi;
-        *(curPtr++) = (intptr)ctx.Rbp;
-		*(curPtr++) = (intptr)ctx.R8;
-		*(curPtr++) = (intptr)ctx.R9;
-		*(curPtr++) = (intptr)ctx.R10;
-		*(curPtr++) = (intptr)ctx.R11;
-		*(curPtr++) = (intptr)ctx.R12;
-		*(curPtr++) = (intptr)ctx.R13;
-		*(curPtr++) = (intptr)ctx.R14;
-		*(curPtr++) = (intptr)ctx.R15;
-#endif
-        int count = curPtr - dataPtr;
-		BF_ASSERT(count == BF_REGISTER_COUNT);
-	}
-}
-
-#endif
-
-int64 abs(int64 val)
-{
-    return llabs(val);
-}
-
-void mkdir(const char* path)
-{
-    mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
-}
+#define lseek64 lseek
+#define ftruncate64 ftruncate
 
-#endif
+#include "../posix/PosixCommon.cpp"

+ 1 - 0
BeefySysLib/platform/ios/BFPlatform.h

@@ -10,6 +10,7 @@
 #endif
 
 #define BF_PLATFORM_IOS
+#define BF_PLATFORM_POSIX
 #define BF_PLATFORM_OPENGL_ES2
 #define BF_PLATFORM_FULLSCREEN
 

+ 5 - 2207
BeefySysLib/platform/linux/LinuxCommon.cpp

@@ -1,2208 +1,6 @@
-#include "Common.h"
-#include "BFPlatform.h"
-#include <sys/stat.h>
-#include <sys/sysinfo.h>
-#include <sys/wait.h>
-#include <dlfcn.h>
-#include <wchar.h>
-#include <fcntl.h>
-#include <time.h>
-#include <link.h>
-#include <dlfcn.h>
-#include <dirent.h>
-#include <syslog.h>
-#include <unistd.h>
-#include <execinfo.h>
-#include <signal.h>
-#include <spawn.h>
-#include "../PlatformInterface.h"
-#include "../PlatformHelper.h"
-#include "../util/CritSect.h"
-#include "../util/Dictionary.h"
-#include "../util/Hash.h"
-#include "backtrace.h"
-#include "backtrace-supported.h"
-#include "../third_party/stb/stb_sprintf.h"
-#include <cxxabi.h>
-#include <random>
+#define BFP_HAS_BACKTRACE
+#define BFP_HAS_EXECINFO
+#define BFP_HAS_PTHREAD_TIMEDJOIN_NP
+#define BFP_HAS_PTHREAD_GETATTR_NP
 
-USING_NS_BF;
-
-struct BfpPipeInfo
-{
-	String mPipePath;
-	int mWriteHandle;
-};
-
-struct BfpFile
-{	
-	BfpPipeInfo* mPipeInfo;
-	int mHandle;	
-	bool mNonBlocking;
-	bool mAllowTimeout;	
-	bool mIsStd;
-
-	BfpFile()
-	{
-		mPipeInfo = NULL;
-		mHandle = -1;		
-		mNonBlocking = false;
-		mAllowTimeout = false;		
-		mIsStd = false;
-	}
-
-	BfpFile(int handle)
-	{	
-		mPipeInfo = NULL;
-		mHandle = handle;		
-		mNonBlocking = false;
-		mAllowTimeout = false;		
-		mIsStd = false;
-	}
-
-	~BfpFile()
-	{
-		delete mPipeInfo;
-	}
-};
-
-BfpTimeStamp BfpToTimeStamp(const timespec& ts)
-{
-    return (int64)(ts.tv_sec * 10000000) + (int64)(ts.tv_nsec / 100) + 116444736000000000;
-}
-
-int gBFPlatformLastError = 0;
-
-uint32 Beefy::BFTickCount()
-{    
-    struct timespec now;
-    if (clock_gettime(CLOCK_MONOTONIC, &now))
-        return 0;
-    return (uint32)((uint64)now.tv_sec * 1000.0 + (uint64)now.tv_nsec / 1000000);
-}
-
-int64 Beefy::EndianSwap(int64 val)
-{
-	return __builtin_bswap64(val);
-}
-
-/*int* GetStdHandle(int32 handleId)
-{
-    if (handleId == STD_INPUT_HANDLE)
-        return (int*)STDIN_FILENO;
-    if (handleId == STD_OUTPUT_HANDLE)
-        return (int*)STDOUT_FILENO;
-    return (int*)STDERR_FILENO;
-}*/
-
-/*int32 GetFileType(HANDLE fileHandle)
-{
-    if (isatty(file->mHandleHandle))
-        return FILE_TYPE_CHAR;
-    return FILE_TYPE_DISK;
-}*/
-
-/*bool WriteFile(HANDLE hFile, void* lpBuffer, uint32 nNumberOfBytesToWrite, uint32* lpNumberOfBytesWritten, OVERLAPPED* lpOverlapped)
-{
-#ifdef BF_PLATFORM_IOS
-    int logType = -1;
-    if (hFile == (int*)STDOUT_FILENO)
-        logType = LOG_WARNING;
-    else if (hFile == (int*)STDERR_FILENO)
-        logType = LOG_ERR;
-    
-    if (logType != -1)
-    {
-        static std::string strOut;
-        strOut.resize(nNumberOfBytesToWrite);
-        memcpy(&strOut[0], lpBuffer, nNumberOfBytesToWrite);
-        if ((strOut[0] != '\r') && (strOut[0] != '\n'))
-            syslog(LOG_WARNING, "%s", strOut.c_str());
-    }
-#endif
-    
-    int writeCount = (int)::write((int)(intptr)hFile, lpBuffer, nNumberOfBytesToWrite);
-    if (writeCount == -1)
-    {
-        //TODO: set gBFPlatformLastError
-        lpNumberOfBytesWritten = 0;
-        return false;
-    }
-    
-    *lpNumberOfBytesWritten = (uint32)writeCount;
-    return true;
-}*/
-
-int64 Beefy::GetFileTimeWrite(const StringImpl& path)
-{
-    struct stat statbuf = {0};
-    int result = stat(path.c_str(), &statbuf);
-    if (result == -1)
-        return 0;
-    
-    //int64 fileTime = 0;
-    //BFSystemTimeToFileTime(statbuf.st_mtime, 0, &fileTime);
-    return statbuf.st_mtime;
-}
-
-/*DWORD GetTimeZoneInformation(TIME_ZONE_INFORMATION* lpTimeZoneInformation)
-{
-    std::wstring tzName0 = Beefy::UTF8Decode(tzname[0]);
-    std::wstring tzName1 = Beefy::UTF8Decode(tzname[1]);
-    
-    bool isDST = false;
-    
-    time_t timeNow;
-    time(&timeNow);
-    tm tmNow = *gmtime(&timeNow);
-    isDST = tmNow.tm_isdst;
-    
-    struct tm checkTM;
-    memset(&checkTM, 0, sizeof(tm));
-    checkTM.tm_mday = 1;
-    checkTM.tm_year = tmNow.tm_year;
-    time_t checkTime = mktime(&checkTM);
-    
-    time_t lastOffset = 0;
-    time_t minOffset = 0;
-    time_t maxOffset = 0;
-    
-    for (int pass = 0; pass < 2; pass++)
-    {
-        int searchDir = 60*60*24;
-        int thresholdCount = 0;
-        
-        while (true)
-        {
-            checkTime += searchDir;
-            
-            tm checkTM = *gmtime(&checkTime);
-            
-            if (checkTM.tm_year != tmNow.tm_year)
-                break; // No DST
-            
-            mktime(&checkTM);
-            
-            time_t offset = checkTM.tm_gmtoff;
-            if (lastOffset != offset)
-            {
-                if (thresholdCount == 0)
-                {
-                    minOffset = offset;
-                    maxOffset = offset;
-                }
-                else if (thresholdCount == 3)
-                {
-                    SYSTEMTIME* sysTimeP = (offset == minOffset) ?
-                    &lpTimeZoneInformation->StandardDate :
-                    &lpTimeZoneInformation->DaylightDate;
-                    
-                    if (offset == minOffset)
-                        tzName0 = Beefy::UTF8Decode(checkTM.tm_zone);
-                    else
-                        tzName1 = Beefy::UTF8Decode(checkTM.tm_zone);
-                    
-                    sysTimeP->wDay = 0;
-                    sysTimeP->wDayOfWeek = 0;
-                    sysTimeP->wYear = checkTM.tm_year + 1900;
-                    sysTimeP->wMonth = checkTM.tm_mon;
-                    sysTimeP->wDay = checkTM.tm_mday + 1;
-                    sysTimeP->wHour = checkTM.tm_hour;
-                    sysTimeP->wMinute = checkTM.tm_min;
-                    sysTimeP->wSecond = checkTM.tm_sec;
-                    sysTimeP->wMilliseconds = 0;
-                    
-                    break;
-                }
-                else
-                {
-                    if (thresholdCount == 1)
-                        searchDir /= -24;
-                    else
-                        searchDir /= -60;
-                    minOffset = std::min(minOffset, offset);
-                    maxOffset = std::max(maxOffset, offset);
-                }
-                thresholdCount++;
-                lastOffset = offset;
-            }
-        }
-    }
-	
-    wcsncpy(lpTimeZoneInformation->StandardName, tzName0.c_str(), 32);
-    wcsncpy(lpTimeZoneInformation->DaylightName, tzName1.c_str(), 32);
-    
-    lpTimeZoneInformation->DaylightBias = (int32)maxOffset;
-    lpTimeZoneInformation->StandardBias = (int32)minOffset;
-    
-    if (minOffset == maxOffset)
-        return 0;
-    return isDST ? 2 : 1;
-}*/
-
-bool Beefy::FileExists(const StringImpl& path, String* outActualName)
-{
-    struct stat statbuf = {0};
-    int result = stat(path.c_str(), &statbuf);
-    if (result != 0)
-        return false;
-    return !S_ISDIR(statbuf.st_mode);
-}
-
-bool Beefy::DirectoryExists(const StringImpl& path, String* outActualName)
-{
-    struct stat statbuf = {0};
-    int result = stat(path.c_str(), &statbuf);
-    if (result != 0)
-        return false;
-    return S_ISDIR(statbuf.st_mode);
-}
-
-uint64 Beefy::BFGetTickCountMicro()
-{
-    struct timespec now;
-    if (clock_gettime(CLOCK_MONOTONIC, &now))
-        return 0;
-    return ((uint64)now.tv_sec * 1000000.0 + (uint64)now.tv_nsec / 1000);
-}
-
-uint64 Beefy::BFGetTickCountMicroFast()
-{
-    return BFGetTickCountMicro();
-}
-
-/*
-int64 abs(int64 val)
-{
-    return llabs(val);
-}
-*/
-void mkdir(const char* path)
-{
-    mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
-}
-
-typedef void(*CrashInfoFunc)();
-
-static CritSect gSysCritSect;
-static String gCrashInfo;
-static Array<CrashInfoFunc> gCrashInfoFuncs;
-
-struct bt_ctx {
-    struct backtrace_state *state;
-    int error;
-};
-
-static void error_callback(void *data, const char *msg, int errnum)
-{
-    struct bt_ctx *ctx = (bt_ctx*)data;
-    fprintf(stderr, "ERROR: %s (%d)", msg, errnum);
-    ctx->error = 1;
-}
-
-static void syminfo_callback (void *data, uintptr_t pc, const char *symname, uintptr_t symval, uintptr_t symsize)
-{
-    char str[4096];
-    if (symname)
-        stbsp_snprintf(str, 4096, "%@ %s\n", pc, symname);
-    else
-        stbsp_snprintf(str, 4096, "%@\n", pc);
-    fputs(str, stderr);
-}
-
-static int full_callback(void *data, uintptr_t pc, const char* filename, int lineno, const char* function)
-{
-    struct bt_ctx *ctx = (bt_ctx*)data;
-    if (function)
-    {
-        int status = -1;
-        char* demangledName = abi::__cxa_demangle(function, NULL, NULL, &status );
-        const char* showName = (demangledName != NULL) ? demangledName : function;
-
-        char str[4096];
-        stbsp_snprintf(str, 4096, "%@ %s %s:%d\n", pc, showName, filename?filename:"??", lineno);
-        fputs(str, stderr);
-
-        if (demangledName != NULL)
-            free(demangledName);
-    }
-    else
-        backtrace_syminfo (ctx->state, pc, syminfo_callback, error_callback, data);
-    return 0;
-}
-
-static int simple_callback(void *data, uintptr_t pc)
-{
-    struct bt_ctx *ctx = (bt_ctx*)data;
-    backtrace_pcinfo(ctx->state, pc, full_callback, error_callback, data);
-    return 0;
-}
-
-static inline void bt(struct backtrace_state *state)
-{
-    struct bt_ctx ctx = {state, 0};
-    //backtrace_print(state, 0, stdout);
-    backtrace_simple(state, 2, simple_callback, error_callback, &ctx);
-}
-
-static String gCmdLine;
-static String gExePath;
-
-static void Crashed()
-{
-    //
-    {
-        AutoCrit autoCrit(gSysCritSect);
-
-        String debugDump;
-
-        debugDump += "**** FATAL APPLICATION ERROR ****\n";
-
-        for (auto func : gCrashInfoFuncs)
-            func();
-
-        if (!gCrashInfo.IsEmpty())
-        {
-            debugDump += gCrashInfo;
-            debugDump += "\n";
-        }
-
-        fprintf(stderr, "%s", debugDump.c_str());
-    }
-
-    struct backtrace_state *state = backtrace_create_state(gExePath.c_str(), BACKTRACE_SUPPORTS_THREADS, error_callback, NULL);
-    bt(state);
-
-    /*void* array[10];
-    size_t size;
-    char** strings;
-    size_t i;
-
-    size = backtrace(array, 10);
-    strings = backtrace_symbols(array, size);
-
-    for (i = 0; i < size; i++)
-        fprintf(stderr, "%s\n", strings[i]);
-
-    free(strings);*/
-
-    exit(1);
-}
-
-static void SigHandler(int sig)
-{
-	//printf("SigHandler paused...\n");	
-
-    const char* sigName = NULL;
-    switch (sig)
-    {
-    case SIGFPE:
-        sigName = "SIGFPE";
-        break;
-    case SIGSEGV:
-        sigName = "SIGSEGV";
-        break;
-    case SIGABRT:
-        sigName = "SIGABRT";
-        break;
-    case SIGILL:
-        sigName = "SIGILL";
-        break;
-    }
-
-    if (sigName != NULL)
-        gCrashInfo += StrFormat("Signal: %s\n", sigName);
-    else
-        gCrashInfo += StrFormat("Signal: %d\n", sig);
-    Crashed();
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpSystem_Init(int version, BfpSystemInitFlags flags)
-{
-    if (version != BFP_VERSION)
-    {
-        BfpSystem_FatalError(StrFormat("Bfp build version '%d' does not match requested version '%d'", BFP_VERSION, version).c_str(), "BFP FATAL ERROR");
-    }
-
-    signal(SIGSEGV, SigHandler);
-    signal(SIGFPE, SigHandler);
-    signal(SIGABRT, SigHandler);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpSystem_SetCommandLine(int argc, char** argv)
-{    
-	char* relPath = argv[0];
-
-	char* cwd = getcwd(NULL, 0);
-	gExePath = GetAbsPath(relPath, cwd);
-	free(cwd);
-
-	for (int i = 0; i < argc; i++)
-	{
-		if (i != 0)
-			gCmdLine.Append(' ');
-
-		String arg = argv[i];
-		if ((arg.Contains(' ')) || (arg.Contains('\"')))
-		{
-			arg.Replace("\"", "\\\"");
-			gCmdLine.Append("\"");
-			gCmdLine.Append(arg);
-			gCmdLine.Append("\"");
-		}
-		else
-			gCmdLine.Append(arg);
-	}
-}
-
-
-BFP_EXPORT void BFP_CALLTYPE BfpSystem_SetCrashReportKind(BfpCrashReportKind crashReportKind)
-{
-
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpSystem_AddCrashInfoFunc(BfpCrashInfoFunc crashInfoFunc)
-{
-    AutoCrit autoCrit(gSysCritSect);
-    gCrashInfoFuncs.Add(crashInfoFunc);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpSystem_AddCrashInfo(const char* str) // Can do at any time, or during CrashInfoFunc callbacks
-{
-    AutoCrit autoCrit(gSysCritSect);
-    gCrashInfo.Append(str);
-}
-
-void BfpSystem_Shutdown()
-{
-
-}
-
-BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_TickCount()
-{
-    return Beefy::BFTickCount();
-}
-
-BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpSystem_GetTimeStamp()
-{
-	struct timeval tv;
-	BfpTimeStamp result = 11644473600LL;
-	gettimeofday(&tv, NULL);
-	result += tv.tv_sec;
-	result *= 10000000LL;
-	result += tv.tv_usec * 10;
-	return result;
-}
-
-BFP_EXPORT uint16 BFP_CALLTYPE BfpSystem_EndianSwap16(uint16 val)
-{
-    return __builtin_bswap16(val);
-}
-
-BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_EndianSwap32(uint32 val)
-{
-    return __builtin_bswap32(val);
-}
-
-BFP_EXPORT uint64 BFP_CALLTYPE BfpSystem_EndianSwap64(uint64 val)
-{
-    return __builtin_bswap64(val);
-}
-
-BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_InterlockedExchange32(uint32* ptr, uint32 val)
-{
-	// __sync_lock_test_and_set only has Acquire semantics, so we need a __sync_synchronize to enforce a full barrier
-	uint32 prevVal = __sync_lock_test_and_set(ptr, val);
-	__sync_synchronize();
-	return prevVal;	
-}
-
-BFP_EXPORT uint64 BFP_CALLTYPE BfpSystem_InterlockedExchange64(uint64* ptr, uint64 val)
-{
-	// __sync_lock_test_and_set only has Acquire semantics, so we need a __sync_synchronize to enforce a full barrier
-	uint64 prevVal = __sync_lock_test_and_set(ptr, val);
-	__sync_synchronize();
-	return prevVal;	
-}
-
-BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_InterlockedExchangeAdd32(uint32* ptr, uint32 val)
-{
-	return __sync_fetch_and_add(ptr, val);
-}
-
-BFP_EXPORT uint64 BFP_CALLTYPE BfpSystem_InterlockedExchangeAdd64(uint64* ptr, uint64 val)
-{
-	return __sync_fetch_and_add(ptr, val);
-}
-
-BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_InterlockedCompareExchange32(uint32* ptr, uint32 oldVal, uint32 newVal)
-{
-    return __sync_val_compare_and_swap(ptr, oldVal, newVal);
-}
-
-BFP_EXPORT uint64 BFP_CALLTYPE BfpSystem_InterlockedCompareExchange64(uint64* ptr, uint64 oldVal, uint64 newVal)
-{
-    return __sync_val_compare_and_swap(ptr, oldVal, newVal);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpSystem_FatalError(const char* error, const char* title)
-{
-    fprintf(stderr, "%s\n", error);
-    fflush(stderr);
-    Crashed();
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpSystem_GetCommandLine(char* outStr, int* inOutStrSize, BfpSystemResult* outResult)
-{
-    TryStringOut(gCmdLine, outStr, inOutStrSize, (BfpResult*)outResult);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpSystem_GetExecutablePath(char* outStr, int* inOutStrSize, BfpSystemResult* outResult)
-{
-	TryStringOut(gExePath, outStr, inOutStrSize, (BfpResult*)outResult);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpSystem_GetEnvironmentStrings(char* outStr, int* inOutStrSize, BfpSystemResult* outResult)
-{
-    String env;
-
-    char** envPtr = environ;
-    while (true)
-    {
-        char* envStr = *envPtr;
-        env.Append(envStr, strlen(envStr) + 1);
-        ++envPtr;
-    }
-
-    TryStringOut(env, outStr, inOutStrSize, (BfpResult*)outResult);
-}
-
-BFP_EXPORT int BFP_CALLTYPE BfpSystem_GetNumLogicalCPUs(BfpSystemResult* outResult)
-{	
-	OUTRESULT(BfpSystemResult_Ok);
-	return get_nprocs_conf();
-}
-
-BFP_EXPORT int64 BFP_CALLTYPE BfpSystem_GetCPUTick()
-{
-	return 10000000;
-}
-
-BFP_EXPORT int64 BFP_CALLTYPE BfpSystem_GetCPUTickFreq()
-{
-	struct timespec now;
-	clock_gettime(CLOCK_MONOTONIC, &now);
-	return (now.tv_sec * 10000000LL) + now.tv_nsec / 100;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpSystem_CreateGUID(BfpGUID* outGuid)
-{
-// 	uuid_t guid;	
-// 	uuid_generate(guid);	
-// 	BfpGUID bfpGuid;
-// 	memcpy(&bfpGuid, guid, 16);
-// 	return bfpGuid;
-
-	uint8* ptr = (uint8*)outGuid;
-
-	std::random_device rd;
-	std::mt19937 gen(rd());
-	std::uniform_int_distribution<uint8> dis(0, 255);
-	for (int i = 0; i < 16; i++)
-		ptr[i] = dis(gen);
-
-	// variant must be 10xxxxxx
-	ptr[8] &= 0xBF;
-	ptr[8] |= 0x80;
-
-	// version must be 0100xxxx
-	ptr[6] &= 0x4F;
-	ptr[6] |= 0x40;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpSystem_GetComputerName(char* outStr, int* inOutStrSize, BfpSystemResult* outResult)
-{
-	char hostName[1024];
-	gethostname(hostName, 1024);
-	TryStringOut(hostName, outStr, inOutStrSize, (BfpResult*)outResult);
-}
-
-// BfpProcess
-
-BFP_EXPORT intptr BFP_CALLTYPE BfpProcess_GetCurrentId()
-{
-    return getpid();
-}
-
-BFP_EXPORT bool BFP_CALLTYPE BfpProcess_IsRemoteMachine(const char* machineName)
-{
-    return false;
-}
-
-BFP_EXPORT BfpProcess* BFP_CALLTYPE BfpProcess_GetById(const char* machineName, int processId, BfpProcessResult* outResult)
-{
-    NOT_IMPL;
-    return NULL;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpProcess_Enumerate(const char* machineName, BfpProcess** outProcesses, int* inOutProcessesSize, BfpProcessResult* outResult)
-{
-    NOT_IMPL;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpProcess_Release(BfpProcess* process)
-{
-    NOT_IMPL;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpProcess_GetMainWindowTitle(BfpProcess* process, char* outTitle, int* inOutTitleSize, BfpProcessResult* outResult)
-{
-    NOT_IMPL;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpProcess_GetProcessName(BfpProcess* process, char* outName, int* inOutNameSize, BfpProcessResult* outResult)
-{
-    NOT_IMPL;
-}
-
-BFP_EXPORT int BFP_CALLTYPE BfpProcess_GetProcessId(BfpProcess* process)
-{
-    NOT_IMPL;
-    return 0;
-}
-
-// BfpSpawn
-
-struct BfpSpawn
-{
-    int mPid;
-    bool mExited;
-    int mStatus;
-    int mStdInFD;
-    int mStdOutFD;
-    int mStdErrFD;
-};
-
-BFP_EXPORT BfpSpawn* BFP_CALLTYPE BfpSpawn_Create(const char* inTargetPath, const char* args, const char* workingDir, const char* env, BfpSpawnFlags flags, BfpSpawnResult* outResult)
-{
-    Beefy::Array<Beefy::StringView> stringViews;
-
-    //printf("Executing: %s %s %x\n", inTargetPath, args, flags);
-
-	if ((workingDir != NULL) && (workingDir[0] != 0))
-	{
-		if (chdir(workingDir) != 0)
-		{
-			//printf("CHDIR failed %s\n", workingDir);
-			OUTRESULT(BfpSpawnResult_UnknownError);
-			return NULL;
-		}
-	}
-
-	String newArgs;
-	String tempFileName;
-
-	if ((flags & BfpSpawnFlag_UseArgsFile) != 0)
-	{
-		char tempFileNameStr[256];
-		int size = 256;
-		BfpFileResult fileResult;
-		BfpFile_GetTempFileName(tempFileNameStr, &size, &fileResult);
-		if (fileResult == BfpFileResult_Ok)
-		{
-			tempFileName = tempFileNameStr;
-
-			BfpFileResult fileResult;
-			BfpFile* file = BfpFile_Create(tempFileNameStr, BfpFileCreateKind_CreateAlways, BfpFileCreateFlag_Write, BfpFileAttribute_Normal, &fileResult);
-			if (file == NULL)
-			{
-				OUTRESULT(BfpSpawnResult_TempFileError);
-				return NULL;
-			}
-
-			if ((flags & BfpSpawnFlag_UseArgsFile_Native) != 0)
-			{
-				UTF16String wStr = UTF8Decode(args);
-
-				if ((flags & BfpSpawnFlag_UseArgsFile_BOM) != 0)
-				{
-					uint8 bom[2] = { 0xFF, 0xFE };
-					BfpFile_Write(file, bom, 2, -1, NULL);
-				}
-
-				BfpFile_Write(file, wStr.c_str(), wStr.length() * 2, -1, NULL);
-			}
-			else
-				BfpFile_Write(file, args, strlen(args), -1, NULL);
-			BfpFile_Release(file);
-
-			newArgs.Append("@");
-			newArgs.Append(tempFileName);
-			if (newArgs.Contains(' '))
-			{
-				newArgs.Insert(0, '\"');
-				newArgs.Append('\"');
-			}
-
-			args = newArgs.c_str();
-		}
-	}
-
-    int32 firstCharIdx = -1;
-    bool inQuote = false;
-
-	String targetPath = inTargetPath;
-	String verb;
-	if ((flags & BfpSpawnFlag_UseShellExecute) != 0)
-	{
-		String target = targetPath;
-		int barPos = (int)target.IndexOf('|');
-		if (barPos != -1)
-		{
-			verb = targetPath.Substring(barPos + 1);
-			targetPath.RemoveToEnd(barPos);
-		}				
-	}
-
-    int32 i = 0;
-    for ( ; true; i++)
-    {
-        char c = args[i];
-        if (c == '\0')
-            break;
-        if ((c == ' ') && (!inQuote))
-        {
-            if (firstCharIdx != -1)
-            {
-                stringViews.Add(Beefy::StringView(args, firstCharIdx, i - firstCharIdx));
-                firstCharIdx = -1;
-            }
-        }
-        else
-        {
-            if (firstCharIdx == -1)
-                firstCharIdx = i;
-            if (c == '"')
-                inQuote = !inQuote;
-            else if ((inQuote) && (c == '\\'))
-            {
-                c = args[i + 1];
-                if (c == '"')
-                    i++;
-            }
-        }
-    }
-    if (firstCharIdx != -1)
-        stringViews.Add(Beefy::StringView(args, firstCharIdx, i - firstCharIdx));
-
-    Beefy::Array<char*> argvArr;
-
-    if ((flags & BfpSpawnFlag_ArgsIncludesTarget) == 0)
-        argvArr.Add(strdup(targetPath.c_str()));
-
-    for (int32 i = 0; i < (int32)stringViews.size(); i++)
-    {
-        Beefy::StringView stringView = stringViews[i];
-        char* str = NULL;
-        for (int32 pass = 0; pass < 2; pass++)
-        {
-            char* strPtr = str;
-
-            int32 strPos = 0;
-            for (int32 char8Idx = 0; char8Idx < stringView.mLength; char8Idx++)
-            {
-                char c = stringView.mPtr[char8Idx];
-                if (c == '"')
-                    inQuote = !inQuote;
-                else
-                {
-                    if ((inQuote) && (c == '\\') && (char8Idx < stringView.mLength - 1))
-                    {
-                        char nextC = stringView.mPtr[char8Idx + 1];
-                        if (nextC == '"')
-                        {
-                            c = nextC;
-                            char8Idx++;
-                        }
-                    }
-                    if (strPtr != NULL)
-                        *(strPtr++) = c;
-                    strPos++;
-                }
-            }
-            if (pass == 0)
-                str = (char*)malloc(strPos + 1);
-            else
-                *(strPtr++) = 0;
-        }
-
-        argvArr.Add(str);
-    }
-    argvArr.Add(NULL);
-
-    char** argv = NULL;
-
-    //pid_t pid = 0;
-    //int status = posix_spawn(&pid, targetPath, NULL, NULL, &argvArr[0], environ);
-
-    Beefy::Array<char*> envArr;
-    if (env != NULL)
-    {
-        char* envPtr = (char*)env;
-        while (true)
-        {
-            if (*envPtr == 0)
-                break;
-
-            envArr.Add(envPtr);
-            envPtr += strlen(envPtr) + 1;
-        }
-    }
-    envArr.Add(NULL);
-
-    int stdInFD[2];
-    int stdOutFD[2];
-    int stdErrFD[2];
-
-	bool failed = false;
-	if ((flags & BfpSpawnFlag_RedirectStdInput) != 0)
-		if (pipe(stdInFD) != 0)
-			failed = true;
-	if ((flags & BfpSpawnFlag_RedirectStdOutput) != 0)
-		if (pipe(stdOutFD) != 0)
-			failed = true;
-	if ((flags & BfpSpawnFlag_RedirectStdError) != 0)
-		if (pipe(stdErrFD) != 0)
-			failed = true;
-	if (failed)
-	{
-		//printf("Pipe failed\n");
-		OUTRESULT(BfpSpawnResult_UnknownError);
-		return NULL;
-	}
-
-    BfpSpawn* spawn;
-    pid_t pid = fork();
-    if (pid == -1) // Error
-    {
-        OUTRESULT(BfpSpawnResult_UnknownError);
-        return NULL;
-    }
-    else if (pid == 0) // Child
-    {
-        if ((flags & BfpSpawnFlag_RedirectStdInput) != 0)
-            while ((dup2(stdInFD[0], STDIN_FILENO) == -1) && (errno == EINTR)) {}
-        if ((flags & BfpSpawnFlag_RedirectStdOutput) != 0)
-            while ((dup2(stdOutFD[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {}
-        if ((flags & BfpSpawnFlag_RedirectStdError) != 0)
-            while ((dup2(stdErrFD[1], STDERR_FILENO) == -1) && (errno == EINTR)) {}
-
-        // If successful then this shouldn't return at all:
-        int result;
-
-        if (env != NULL)
-            result = execve(targetPath.c_str(), (char* const*)&argvArr[0], (char* const*)&envArr[0]);
-        else
-            result = execv(targetPath.c_str(), (char* const*)&argvArr[0]);
-
-        printf("Couldn't execute %s\n", targetPath.c_str());
-
-        exit(-1);
-    }
-    else // Parent
-    {
-        spawn = new BfpSpawn();
-
-        if ((flags & BfpSpawnFlag_RedirectStdInput) != 0)
-        {
-            spawn->mStdInFD = stdInFD[1];
-            close(stdInFD[0]);
-        }
-        else
-            spawn->mStdInFD = 0;
-
-        if ((flags & BfpSpawnFlag_RedirectStdOutput) != 0)
-        {
-            spawn->mStdOutFD = stdOutFD[0];
-            close(stdOutFD[1]);
-        }
-        else
-            spawn->mStdOutFD = 0;
-
-        if ((flags & BfpSpawnFlag_RedirectStdError) != 0)
-        {
-            spawn->mStdErrFD = stdErrFD[0];
-            close(stdErrFD[1]);
-        }
-        else
-            spawn->mStdErrFD = 0;
-    }
-
-    for (auto val : argvArr)
-        free(val);
-
-    //printf("Spawn pid:%d status:%d\n", pid, status);
-    spawn->mPid = pid;
-    spawn->mExited = false;
-    spawn->mStatus = 0;
-
-    return spawn;
-}
-
-void BfpSpawn_Release(BfpSpawn* spawn)
-{
-    // We don't support 'detaching' currently- this can create zombie processes since we
-    //  don't have a reaper strategy
-    BfpSpawn_WaitFor(spawn, -1, NULL, NULL);
-
-    delete spawn;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpSpawn_GetStdHandles(BfpSpawn* spawn, BfpFile** outStdIn, BfpFile** outStdOut, BfpFile** outStdErr)
-{
-    if (outStdIn != NULL)
-    {
-        *outStdIn = new BfpFile(spawn->mStdInFD);
-        spawn->mStdInFD = 0;
-    }
-
-    if (outStdOut != NULL)
-    {
-        *outStdOut = new BfpFile(spawn->mStdOutFD);
-        spawn->mStdOutFD = 0;
-    }
-
-    if (outStdErr != NULL)
-    {
-        *outStdErr = new BfpFile(spawn->mStdErrFD);
-        spawn->mStdErrFD = 0;
-    }
-}
-
-bool BfpSpawn_WaitFor(BfpSpawn* spawn, int waitMS, int* outExitCode, BfpSpawnResult* outResult)
-{
-    OUTRESULT(BfpSpawnResult_Ok);
-    if (!spawn->mExited)
-    {
-        int flags = 0;
-        if (waitMS != -1)
-        {
-            flags = WNOHANG;
-        }
-        //TODO: Implement values other than 0 or -1 for waitMS?
-
-        pid_t result = waitpid(spawn->mPid, &spawn->mStatus, flags);
-        if (result != spawn->mPid)
-            return false;
-
-        spawn->mExited = true;
-    }
-
-    if (!WIFEXITED(spawn->mStatus) && !WIFSIGNALED(spawn->mStatus))
-        return false;
-
-    if (outExitCode != NULL)
-        *outExitCode = WEXITSTATUS(spawn->mStatus);
-    return true;
-}
-
-// BfpFileWatcher
-
-BFP_EXPORT BfpFileWatcher* BFP_CALLTYPE BfpFileWatcher_WatchDirectory(const char* path, BfpDirectoryChangeFunc callback, BfpFileWatcherFlags flags, void* userData, BfpFileResult* outResult)
-{
-    NOT_IMPL;
-    return NULL;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFileWatcher_Release(BfpFileWatcher* fileWatcher)
-{
-    NOT_IMPL;
-}
-
-// BfpThread
-
-struct BfpThread
-{
-    bool mPThreadReleased;
-    BfpThreadStartProc mStartProc;
-    void* mThreadParam;
-
-    pthread_t mPThread;
-    int mRefCount;
-    int mPriority;   
-
-    BfpThread()
-    {
-    }
-
-    void Release()
-    {
-        int refCount = __sync_fetch_and_sub(&mRefCount, 1) - 1;
-        if (refCount == 0)
-            delete this;
-    }
-};
-
-struct BfpThreadInfo
-{
-    intptr mStackBase;
-    int mStackLimit;
-};
-
-static __thread BfpThread* gCurrentThread;
-static __thread BfpThreadInfo gCurrentThreadInfo;
-
-void* ThreadFunc(void* threadParam)
-{
-    BfpThread* thread = (BfpThread*)threadParam;
-    gCurrentThread = thread;
-    thread->mStartProc(thread->mThreadParam);
-    thread->Release();
-}
-
-BFP_EXPORT BfpThread* BFP_CALLTYPE BfpThread_Create(BfpThreadStartProc startProc, void* threadParam, intptr stackSize, BfpThreadCreateFlags flags, BfpThreadId* outThreadId)
-{
-    BfpThread* thread = new BfpThread();
-    thread->mPThreadReleased = false;
-    thread->mStartProc = startProc;
-    thread->mThreadParam = threadParam;
-    thread->mRefCount = 2;
-    thread->mPriority = 0;
-
-    BF_ASSERT(sizeof(pthread_t) <= sizeof(void*));    
-    pthread_attr_t params;
-    pthread_attr_init(&params);
-    pthread_attr_setstacksize(&params, stackSize);
-    //pthread_attr_setdetachstate(&params,PTHREAD_CREATE_DETACHED);
-
-    pthread_create(&thread->mPThread, &params, ThreadFunc, (void*)thread);
-
-    pthread_attr_destroy(&params);
-    
-    if (outThreadId != NULL)
-        *outThreadId = (BfpThreadId)thread->mPThread;
-
-    return thread;
-}
-
-#define FIXTHREAD() \
-    pthread_t pt; \
-    if (((intptr)thread & 1) != 0) \
-    { \
-        pt = (pthread_t)((intptr)thread & ~3); \
-        thread = NULL; \
-    } else \
-    pt = thread->mPThread
-
-BFP_EXPORT void BFP_CALLTYPE BfpThread_Release(BfpThread* thread)
-{
-    FIXTHREAD();
-    if (thread == NULL)
-        return;
-
-    if (!thread->mPThreadReleased)
-    {
-        pthread_detach(thread->mPThread);
-        thread->mPThreadReleased = true;
-    }
-    thread->Release();
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpThread_SetName(BfpThread* thread, const char* name, BfpThreadResult* outResult)
-{
-	OUTRESULT(BfpThreadResult_Ok);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpThread_GetName(BfpThread* thread, char* outName, int* inOutNameSize, BfpThreadResult* outResult)
-{
-	String str = "";
-	TryStringOut(str, outName, inOutNameSize, (BfpResult*)outResult);
-}
-
-BFP_EXPORT BfpThread* BFP_CALLTYPE BfpThread_GetCurrent()
-{
-    if (gCurrentThread == NULL)
-    {
-        // Not a "true" BfpThread, this is either the main thread or a thread we didn't create
-        return (BfpThread*)((intptr)pthread_self() | 1);
-    }
-    return gCurrentThread;
-}
-
-BFP_EXPORT BfpThreadId BFP_CALLTYPE BfpThread_GetCurrentId()
-{
-    if (gCurrentThread == NULL)
-    {
-        return (BfpThreadId)((intptr)pthread_self());
-    }
-    return (BfpThreadId)gCurrentThread->mPThread;
-}
-
-BFP_EXPORT bool BFP_CALLTYPE BfpThread_WaitFor(BfpThread* thread, int waitMS)
-{
-    FIXTHREAD();
-
-    if (waitMS == -1)
-    {
-        pthread_join(pt, NULL);
-        thread->mPThreadReleased = true;
-        return true;
-    }
-
-    struct timespec waitTime;
-    waitTime.tv_sec = waitMS / 1000;
-    waitTime.tv_nsec = (waitMS % 1000) * 1000000;
-    int result = pthread_timedjoin_np(pt, NULL, &waitTime);
-    if (result == 0)
-    {
-        if (thread != NULL)
-            thread->mPThreadReleased = true;
-        return true;
-    }
-    return false;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpSpawn_Kill(BfpSpawn* spawn, int exitCode, BfpKillFlags killFlags, BfpSpawnResult* outResult)
-{
-	//TODO: Implement
-	OUTRESULT(BfpSpawnResult_UnknownError);
-}
-
-BFP_EXPORT BfpThreadPriority BFP_CALLTYPE BfpThread_GetPriority(BfpThread* thread, BfpThreadResult* outResult)
-{
-    FIXTHREAD();
-
-    OUTRESULT(BfpThreadResult_Ok);
-    if (thread == NULL)
-        return (BfpThreadPriority)0;
-
-    return (BfpThreadPriority)thread->mPriority;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpThread_SetPriority(BfpThread* thread, BfpThreadPriority threadPriority, BfpThreadResult* outResult)
-{
-    // In effect, we have two 'nice' settings: 0 (normal) or 10 (low)
-    //  High-priority settings just don't do anything
-    //pid_t tid = syscall(SYS_gettid);
-    //int ret = setpriority(PRIO_PROCESS, tid, -std::min(nPriority, 0) * 10);
-    OUTRESULT(BfpThreadResult_Ok);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpThread_Suspend(BfpThread* thread, BfpThreadResult* outResult)
-{
-    NOT_IMPL;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpThread_Resume(BfpThread* thread, BfpThreadResult* outResult)
-{
-    NOT_IMPL;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpThread_GetIntRegisters(BfpThread* thread, intptr* outStackPtr, intptr* outIntRegs, int* inOutIntRegCount, BfpThreadResult* outResult)
-{
-    NOT_IMPL;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpThread_GetStackInfo(BfpThread* thread, intptr* outStackBase, int* outStackLimit, BfpThreadResult* outResult)
-{
-    if (gCurrentThreadInfo.mStackBase == 0)
-    {
-        void* stackBase = 0;
-        size_t stackLimit = 0;
-
-        pthread_attr_t attr;
-        pthread_getattr_np(pthread_self(), &attr);
-        pthread_attr_getstack(&attr, &stackBase, &stackLimit);
-
-        gCurrentThreadInfo.mStackBase = (intptr)stackBase + stackLimit;
-        gCurrentThreadInfo.mStackLimit = (int)stackLimit;
-        pthread_attr_destroy(&attr);
-    }
-
-    *outStackBase = gCurrentThreadInfo.mStackBase;
-    *outStackLimit = gCurrentThreadInfo.mStackLimit;
-
-	OUTRESULT(BfpThreadResult_Ok);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpThread_Sleep(int sleepMS)
-{
-    usleep(sleepMS * 1000);
-}
-
-BFP_EXPORT bool BFP_CALLTYPE BfpThread_Yield()
-{
-    return sched_yield() == 0;
-}
-
-struct BfpCritSect
-{
-    pthread_mutex_t mPMutex;
-};
-
-BFP_EXPORT BfpCritSect* BFP_CALLTYPE BfpCritSect_Create()
-{
-    BfpCritSect* critSect = new BfpCritSect();
-
-    pthread_mutexattr_t     attributes;        
-    pthread_mutexattr_init(&attributes);
-    pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_RECURSIVE);
-    pthread_mutex_init(&critSect->mPMutex, &attributes);
-    pthread_mutexattr_destroy(&attributes);
-
-    return critSect;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpCritSect_Release(BfpCritSect* critSect)
-{
-    pthread_mutex_destroy(&critSect->mPMutex);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpCritSect_Enter(BfpCritSect* critSect)
-{
-    pthread_mutex_lock(&critSect->mPMutex);
-}
-
-BFP_EXPORT bool BFP_CALLTYPE BfpCritSect_TryEnter(BfpCritSect* critSect, int waitMS)
-{
-    if (waitMS == -1)
-    {
-        BfpCritSect_Enter(critSect);
-        return true;
-    }
-    else if (waitMS == 0)
-    {
-        return pthread_mutex_trylock(&critSect->mPMutex) == 0;
-    }
-    
-    uint32 start = Beefy::BFTickCount();
-    while ((int)(Beefy::BFTickCount() - start) < waitMS)
-    {
-        if (pthread_mutex_trylock(&critSect->mPMutex) == 0)
-        {                
-            return true;
-        }
-    }
-    return false;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpCritSect_Leave(BfpCritSect* critSect)
-{
-    pthread_mutex_unlock(&critSect->mPMutex);
-}
-
-BFP_EXPORT BfpTLS* BFP_CALLTYPE BfpTLS_Create()
-{
-    pthread_key_t key = 0;
-    pthread_key_create(&key, NULL);
-    return (BfpTLS*)(intptr)key;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpTLS_Release(BfpTLS* tls)
-{
-    pthread_key_delete((pthread_key_t)(intptr)tls);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpTLS_SetValue(BfpTLS* tls, void* value)
-{
-    pthread_setspecific((pthread_key_t)(intptr)tls, value);
-}
-
-BFP_EXPORT void* BFP_CALLTYPE BfpTLS_GetValue(BfpTLS* tls)
-{
-    return pthread_getspecific((pthread_key_t)(intptr)tls);
-}
-
-struct BfpEvent
-{
-    pthread_mutex_t mMutex;
-    pthread_cond_t mCondVariable;
-    bool mSet;
-    bool mManualReset;
-};
-
-BFP_EXPORT BfpEvent* BFP_CALLTYPE BfpEvent_Create(BfpEventFlags flags)
-{
-    BfpEvent* event = new BfpEvent();
-    pthread_mutex_init(&event->mMutex, NULL);
-    pthread_cond_init(&event->mCondVariable, NULL);
-    event->mSet = (flags & (BfpEventFlag_InitiallySet_Auto | BfpEventFlag_InitiallySet_Manual)) != 0;
-    event->mManualReset = (flags & BfpEventFlag_InitiallySet_Manual) != 0;
-    return event;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpEvent_Release(BfpEvent* event)
-{
-    pthread_cond_destroy(&event->mCondVariable);
-    pthread_mutex_destroy(&event->mMutex);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpEvent_Set(BfpEvent* event, bool requireManualReset)
-{
-    pthread_mutex_lock(&event->mMutex);
-    event->mSet = true;
-    if (requireManualReset)
-        event->mManualReset = true;
-    if (event->mManualReset)
-        pthread_cond_broadcast(&event->mCondVariable);
-    else
-        pthread_cond_signal(&event->mCondVariable);
-    pthread_mutex_unlock(&event->mMutex);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpEvent_Reset(BfpEvent* event, BfpEventResult* outResult)
-{
-    event->mSet = false;
-    event->mManualReset = false;
-}
-
-BFP_EXPORT bool BFP_CALLTYPE BfpEvent_WaitFor(BfpEvent* event, int waitMS)
-{
-    int result = pthread_mutex_lock(&event->mMutex);
-    BF_ASSERT(result == 0);
-    while (!event->mSet)
-    {
-        if (waitMS == -1)
-        {
-            pthread_cond_wait(&event->mCondVariable, &event->mMutex);
-        }
-        else
-        {
-            timespec ts;
-            clock_gettime(CLOCK_REALTIME, &ts);
-            ts.tv_sec += waitMS / 1000;
-            ts.tv_nsec += (waitMS % 1000) * 1000000;
-            
-            result = pthread_cond_timedwait(&event->mCondVariable, &event->mMutex, &ts);
-            
-            if (waitMS == (uint32)-1)
-                BF_ASSERT(result == 0);
-            
-            if (result != 0)
-            {
-                // Timeout                
-                pthread_mutex_unlock(&event->mMutex);
-                return false;
-            }
-        }
-    }
-    if (!event->mManualReset)
-        event->mSet = false;        
-    pthread_mutex_unlock(&event->mMutex);
-    return true;
-}
-
-BFP_EXPORT BfpDynLib* BFP_CALLTYPE BfpDynLib_Load(const char* fileName)
-{
-    BfpDynLib* mod = NULL;
-    
-    static const char* prefixes[] = {NULL, "lib"};
-    static const char* suffixes[] = {NULL, ".so", ".dylib"};
-    
-    for (int prefixIdx = 0; prefixIdx < sizeof(prefixes)/sizeof(prefixes[0]); prefixIdx++)
-    {
-        for (int suffixIdx = 0; suffixIdx < sizeof(suffixes)/sizeof(suffixes[0]); suffixIdx++)
-        {
-            const char* prefix = prefixes[prefixIdx];
-            const char* suffix = suffixes[suffixIdx];
-            
-            Beefy::String checkName = fileName;
-            if (prefix != NULL)
-                checkName = Beefy::String(prefix) + checkName;
-            if (suffix != NULL)
-            {
-                int dotPos = checkName.LastIndexOf('.');
-                if (dotPos != -1)
-                    checkName.RemoveToEnd(dotPos);
-                checkName += suffix;
-            }
-            
-            mod = (BfpDynLib*)dlopen(checkName.c_str(), RTLD_LAZY);
-            if (mod != NULL)
-                return mod;
-        }
-    }
-    
-     /*mod = (BfpDynLib*)dlopen("/var/Beef/qt-build/Debug/bin/libIDEHelper.so", RTLD_LAZY);;
-     if (mod == NULL)
-     {
-         printf("Err: %s\n", dlerror());
-         fflush(stdout);
-     }*/
-
-    return NULL;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpDynLib_Release(BfpDynLib* lib)
-{
-    dlclose((void*)lib);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpDynLib_GetFilePath(BfpDynLib* lib, char* outPath, int* inOutPathSize, BfpLibResult* outResult)
-{
-    Beefy::String path;
-
-    link_map* linkMap = NULL;
-    dlinfo((void*)lib, RTLD_DI_LINKMAP, &linkMap);
-    if (linkMap == NULL)
-    {
-        OUTRESULT(BfpLibResult_UnknownError);
-        return;
-    }
-
-    path = linkMap->l_name;
-    TryStringOut(path, outPath, inOutPathSize, (BfpResult*)outResult);
-}
-
-BFP_EXPORT void* BFP_CALLTYPE BfpDynLib_GetProcAddress(BfpDynLib* lib, const char* name)
-{
-    return dlsym((void*)lib, name);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpDirectory_Create(const char* path, BfpFileResult* outResult)
-{
-    if (mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0)
-    {
-        switch (errno)
-        {
-        case EEXIST:
-            OUTRESULT(BfpFileResult_AlreadyExists);
-            break;
-        case ENOENT:
-            OUTRESULT(BfpFileResult_NotFound);
-            break;
-        default:
-            OUTRESULT(BfpFileResult_UnknownError);
-            break;
-        }
-    }
-    else    
-        OUTRESULT(BfpFileResult_Ok);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpDirectory_Rename(const char* oldName, const char* newName, BfpFileResult* outResult)
-{
-    NOT_IMPL;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpDirectory_Delete(const char* path, BfpFileResult* outResult)
-{
-    if (rmdir(path) != 0)
-    {
-        switch (errno)
-        {
-        case ENOENT:
-            OUTRESULT(BfpFileResult_NotFound);
-            break;
-        default:
-            OUTRESULT(BfpFileResult_UnknownError);
-            break;
-        }
-    }
-    else    
-        OUTRESULT(BfpFileResult_Ok);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpDirectory_GetCurrent(char* outPath, int* inOutPathSize, BfpFileResult* outResult)
-{
-    char* str = getcwd(NULL, 0);
-    Beefy::String path = str;
-    free(str);
-    TryStringOut(path, outPath, inOutPathSize, (BfpResult*)outResult);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpDirectory_SetCurrent(const char* path, BfpFileResult* outResult)
-{    
-    if (chdir(path) != 0)
-        OUTRESULT(BfpFileResult_NotFound);  
-    else    
-        OUTRESULT(BfpFileResult_Ok);
-}
-
-BFP_EXPORT bool BFP_CALLTYPE BfpDirectory_Exists(const char* path)
-{
-    struct stat statbuf = {0};
-    int result = stat(path, &statbuf);
-    if (result != 0)
-        return false;
-    return S_ISDIR(statbuf.st_mode);   
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpDirectory_GetSysDirectory(BfpSysDirectoryKind sysDirKind, char* outPath, int* inOutPathLen, BfpFileResult* outResult)
-{
-	String path = "~";
-	TryStringOut(path, outPath, inOutPathLen, (BfpResult*)outResult);
-}
-
-BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_Create(const char* inName, BfpFileCreateKind createKind, BfpFileCreateFlags createFlags, BfpFileAttributes createdFileAttrs, BfpFileResult* outResult)
-{    
-	auto _DoCreate = [&](String& name)
-	{
-		int flags = 0;
-		int mode = 0;
-		int pipePairHandle = -1;
-
-		if ((createFlags & (BfpFileCreateFlag_Read | BfpFileCreateFlag_Write)) == (BfpFileCreateFlag_Read | BfpFileCreateFlag_Write))
-			flags |= O_RDWR;
-		else if ((createFlags & BfpFileCreateFlag_Read) != 0)
-			flags |= O_RDONLY;
-		else if ((createFlags & BfpFileCreateFlag_Write) != 0)
-			flags |= O_WRONLY;
-
-		if ((createFlags & BfpFileCreateFlag_Append) != 0)
-			flags |= O_APPEND;
-		if ((createFlags & BfpFileCreateFlag_Truncate) != 0)
-			flags |= O_TRUNC;
-		if ((createFlags & (BfpFileCreateFlag_NonBlocking | BfpFileCreateFlag_AllowTimeouts)) != 0)
-			flags |= O_NONBLOCK;
-
-		if ((createFlags & BfpFileCreateFlag_Pipe) != 0)
-		{
-			name = "/tmp/" + name;
-			if ((createKind == BfpFileCreateKind_CreateAlways) ||
-				(createKind == BfpFileCreateKind_CreateIfNotExists))
-			{
-				for (int pass = 0; pass < 2; pass++)
-				{
-					int result = mknod(name.c_str(), S_IFIFO | 0666, 0);
-					if (result == 0)
-						break;
-
-					int err = errno;
-					if (err == EEXIST)
-					{
-						err = remove(name.c_str());
-						if (err == 0)
-							continue;
-						OUTRESULT(BfpFileResult_AlreadyExists);
-						return -1;
-					}
-
-					OUTRESULT(BfpFileResult_UnknownError);
-					return -1;
-				}
-			}
-		}
-		else
-		{
-			if (createKind == BfpFileCreateKind_CreateAlways)
-				flags |= O_CREAT;
-			else if (createKind == BfpFileCreateKind_CreateIfNotExists)
-				flags |= O_CREAT | O_EXCL;
-		}
-
-		mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
-
-		int result = open(name.c_str(), flags, mode);
-        //printf("BfpFile_Create %s %d %d %d\n", name.c_str(), result, flags, mode);
-
-		if (result <= 0)
-		{
-			switch (errno)
-			{
-			case EEXIST:
-				OUTRESULT(BfpFileResult_AlreadyExists);
-				break;
-			case ENOENT:
-				OUTRESULT(BfpFileResult_NotFound);
-				break;
-			case EACCES:
-				OUTRESULT(BfpFileResult_AccessError);
-				break;
-			default:
-				OUTRESULT(BfpFileResult_UnknownError);
-				break;
-			}
-			return -1;
-		}
-        return result;
-	};
-
-	BfpFile* bfpFile = NULL;
-
-	int result;	
-	if ((createFlags & BfpFileCreateFlag_Pipe) != 0)
-	{
-		int readHandle;
-		int writeHandle;
-
-		String name = inName;
-		String altName = name + "__";
-
-		bool isCreating = false;
-		if ((createKind == BfpFileCreateKind_CreateAlways) ||
-			(createKind == BfpFileCreateKind_CreateIfNotExists))
-		{
-			readHandle = _DoCreate(name);
-			writeHandle = _DoCreate(altName);
-			isCreating = true;
-		}
-		else
-		{
-			readHandle = _DoCreate(altName);
-			writeHandle = _DoCreate(name);
-		}
-
-		if ((readHandle != -1) && (writeHandle != -1))
-		{
-			OUTRESULT(BfpFileResult_Ok);
-
-			BfpPipeInfo* pipeInfo = new BfpPipeInfo();
-			pipeInfo->mWriteHandle = writeHandle;		
-			if (isCreating)
-				pipeInfo->mPipePath = name;
-			bfpFile = new BfpFile();
-			bfpFile->mHandle = readHandle;			
-			bfpFile->mPipeInfo = pipeInfo;
-		}
-		else
-		{
-			if (readHandle != -1)
-				close(readHandle);
-			if (writeHandle != -1)
-				close(writeHandle);
-
-			return NULL;
-		}
-	}
-	else
-	{
-		String name = inName;
-		int handle = _DoCreate(name);
-		if (handle == -1)
-			return NULL;
-
-		OUTRESULT(BfpFileResult_Ok);
-		bfpFile = new BfpFile();
-		bfpFile->mHandle = handle;
-	}
-
-	OUTRESULT(BfpFileResult_Ok);	
-	if ((createFlags & (BfpFileCreateFlag_NonBlocking | BfpFileCreateFlag_AllowTimeouts)) != 0)
-        bfpFile->mNonBlocking = true;
-	if ((createFlags & BfpFileCreateFlag_AllowTimeouts) != 0)       
-        bfpFile->mAllowTimeout = true;	
-    return bfpFile;
-}
-
-BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_GetStd(BfpFileStdKind kind, BfpFileResult* outResult)
-{
-	int h = -1;
-	switch (kind)
-	{
-	case BfpFileStdKind_StdOut:
-		h = STDOUT_FILENO;
-		break;
-	case BfpFileStdKind_StdError:
-		h = STDERR_FILENO;
-		break;
-	case BfpFileStdKind_StdIn:
-		h = STDIN_FILENO;
-		break;
-	}
-	if (h == -1)
-	{
-		OUTRESULT(BfpFileResult_NotFound);
-		return NULL;
-	}
-
-	BfpFile* bfpFile = new BfpFile();
-	bfpFile->mHandle = h;
-	bfpFile->mIsStd = true;
-
-	return bfpFile;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFile_Release(BfpFile* file)
-{	
-	if ((file->mHandle != -1) && (!file->mIsStd))
-		close(file->mHandle);
-	if (file->mPipeInfo != NULL)
-	{
-		if (file->mPipeInfo->mWriteHandle != -1)
-			close(file->mPipeInfo->mWriteHandle);
-		
-		if (!file->mPipeInfo->mPipePath.IsEmpty())
-		{
-			int worked = remove(file->mPipeInfo->mPipePath.c_str());
-			remove((file->mPipeInfo->mPipePath + "__").c_str());
-			//printf("Removing %s %d\n", file->mPipeInfo->mPipePath.c_str(), worked);
-		}
-	}	
-
-	delete file;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFile_Close(BfpFile* file, BfpFileResult* outResult)
-{
-	if (file->mHandle != -1)
-	{
-		close(file->mHandle);
-		file->mHandle = -1;
-		if (file->mPipeInfo != NULL)
-		{
-			close(file->mPipeInfo->mWriteHandle);
-			file->mPipeInfo->mWriteHandle = -1;
-		}
-
-		OUTRESULT(BfpFileResult_Ok);
-	}
-	else
-		OUTRESULT(BfpFileResult_UnknownError);
-}
-
-BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Write(BfpFile* file, const void* buffer, intptr size, int timeoutMS, BfpFileResult* outResult)
-{   
-	int writeHandle = file->mHandle;
-	if (file->mPipeInfo != NULL)
-		writeHandle = file->mPipeInfo->mWriteHandle;
-
-    intptr writeCount = ::write(writeHandle, buffer, size);
-// 	if ((writeCount > 0) && (file->mIsPipe))
-// 	{
-// 		::fsync(file->mHandle);
-// 	}
-
-    if (writeCount < 0)
-        OUTRESULT(BfpFileResult_UnknownError);
-    else if (writeCount != size)
-        OUTRESULT(BfpFileResult_PartialData);
-    else
-        OUTRESULT(BfpFileResult_Ok);
-    return writeCount;
-}
-
-BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Read(BfpFile* file, void* buffer, intptr size, int timeoutMS, BfpFileResult* outResult)
-{
-	if (file->mNonBlocking)
-	{
-		if (!file->mAllowTimeout)		
-			timeoutMS = -1;		
-		
-		timeval timeout;
-		timeout.tv_sec = 0;
-		timeout.tv_usec = timeoutMS * 1000;
-
-		fd_set readFDSet;
-		FD_ZERO(&readFDSet);
-		FD_SET(file->mHandle, &readFDSet);
-
-		fd_set errorFDSet;
-		FD_ZERO(&errorFDSet);
-		FD_SET(file->mHandle, &errorFDSet);
-
-		if (select(file->mHandle + 1, &readFDSet, NULL, &errorFDSet, (timeoutMS == -1) ? NULL : &timeout) < 0)
-		{
-			OUTRESULT(BfpFileResult_Timeout);
-			return 0;
-		}
-	}
-
-    intptr readCount = ::read(file->mHandle, buffer, size);
-    if (readCount < 0)
-        OUTRESULT(BfpFileResult_UnknownError);
-    else if (readCount != size)
-        OUTRESULT(BfpFileResult_PartialData);
-    else
-        OUTRESULT(BfpFileResult_Ok);
-    return readCount;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFile_Flush(BfpFile* file)
-{
-    ::fsync(file->mHandle);
-}
-
-BFP_EXPORT int64 BFP_CALLTYPE BfpFile_GetFileSize(BfpFile* file)
-{
-    int64 oldPos = (int64)lseek64(file->mHandle, 0, SEEK_CUR);
-    int64 size = (int64)lseek64(file->mHandle, 0, SEEK_END);
-    lseek64(file->mHandle, oldPos, SEEK_SET);
-    return (int64)size;
-}
-
-BFP_EXPORT int64 BFP_CALLTYPE BfpFile_Seek(BfpFile* file, int64 offset, BfpFileSeekKind seekKind)
-{
-    int whence;
-    if (seekKind == BfpFileSeekKind_Absolute)        
-        whence = SEEK_SET;
-    else if (seekKind == BfpFileSeekKind_Relative)
-        whence = SEEK_CUR;
-    else
-        whence = SEEK_END;
-    return lseek64(file->mHandle, offset, whence);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFile_Truncate(BfpFile* file)
-{
-    int64 curPos = (int64)lseek64(file->mHandle, 0, SEEK_CUR);
-	if (ftruncate64(file->mHandle, curPos) != 0)
-	{
-		//TODO: Report error?
-	}
-}
-
-BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpFile_GetTime_LastWrite(const char* path)
-{
-    struct stat statbuf = {0};
-    int result = stat(path, &statbuf);
-    if (result != 0)
-        return 0;    
-    return statbuf.st_mtime;
-}
-
-BFP_EXPORT BfpFileAttributes BFP_CALLTYPE BfpFile_GetAttributes(const char* path, BfpFileResult* outResult)
-{
-    NOT_IMPL;
-    return (BfpFileAttributes)0;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFile_SetAttributes(const char* path, BfpFileAttributes attribs, BfpFileResult* outResult)
-{
-    NOT_IMPL;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFile_Copy(const char* oldPath, const char* newPath, BfpFileCopyKind copyKind, BfpFileResult* outResult)
-{
-    int fd_to, fd_from;
-    char buf[4096];
-    ssize_t nread;  
-
-    fd_from = open(oldPath, O_RDONLY);
-    if (fd_from < 0)
-    {
-        OUTRESULT(BfpFileResult_NotFound);
-        return;
-    }
-
-    int flags = O_WRONLY | O_CREAT;
-    if (copyKind == BfpFileCopyKind_IfNotExists)
-        flags |= O_EXCL;
-
-    fd_to = open(newPath, flags, 0666);
-    if (fd_to < 0)
-    {
-        if (errno == EEXIST)
-        {
-            OUTRESULT(BfpFileResult_AlreadyExists);
-            goto out_error;
-        }
-
-        OUTRESULT(BfpFileResult_UnknownError);
-        goto out_error;
-    }
-
-    while (nread = read(fd_from, buf, sizeof buf), nread > 0)
-    {
-        char *out_ptr = buf;
-        ssize_t nwritten;
-
-        do {
-            nwritten = write(fd_to, out_ptr, nread);
-
-            if (nwritten >= 0)
-            {
-                nread -= nwritten;
-                out_ptr += nwritten;
-            }
-            else if (errno != EINTR)
-            {
-                OUTRESULT(BfpFileResult_UnknownError);
-                goto out_error;
-            }
-        } while (nread > 0);
-    }
-
-    if (nread == 0)
-    {
-        if (close(fd_to) < 0)
-        {
-            fd_to = -1;
-            OUTRESULT(BfpFileResult_UnknownError);
-            goto out_error;
-        }
-        close(fd_from);
-
-        /* Success! */
-        OUTRESULT(BfpFileResult_Ok);
-        return;
-    }
-
-out_error:
-    close(fd_from);
-    if (fd_to >= 0)
-        close(fd_to);   
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFile_Rename(const char* oldPath, const char* newPath, BfpFileResult* outResult)
-{
-    NOT_IMPL;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFile_Delete(const char* path, BfpFileResult* outResult)
-{
-    if (remove(path) != 0)
-    {
-        switch (errno)
-        {
-        case ENOENT:
-            OUTRESULT(BfpFileResult_NotFound);
-            break;
-        default:
-            OUTRESULT(BfpFileResult_UnknownError);
-            break;
-        }
-    }
-    else    
-        OUTRESULT(BfpFileResult_Ok);
-}
-
-BFP_EXPORT bool BFP_CALLTYPE BfpFile_Exists(const char* path)
-{
-    struct stat statbuf = {0};
-    int result = stat(path, &statbuf);
-    if (result != 0)
-        return false;
-    return !S_ISDIR(statbuf.st_mode);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFile_GetTempPath(char* outPath, int* inOutPathSize, BfpFileResult* outResult)
-{
-    NOT_IMPL;
-}
-
-static const char cHash64bToChar[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
-    'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F',
-    'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
-    'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_' };
-static void HashEncode64(StringImpl& outStr, uint64 val)
-{
-    for (int i = 0; i < 10; i++)
-    {
-        int charIdx = (int)((val >> (i * 6)) & 0x3F) - 1;
-        if (charIdx != -1)
-            outStr.Append(cHash64bToChar[charIdx]);
-    }
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFile_GetTempFileName(char* outName, int* inOutNameSize, BfpFileResult* outResult)
-{
-    static uint32 uniqueIdx = 0;
-    BfpSystem_InterlockedExchangeAdd32(&uniqueIdx, 1);
-
-    Beefy::HashContext ctx;
-    ctx.Mixin(uniqueIdx);
-    ctx.Mixin(getpid());
-    ctx.Mixin(Beefy::BFGetTickCountMicro());
-
-    uint64 hash = ctx.Finish64();
-
-    String str = "/tmp/bftmp_";
-    HashEncode64(str, hash);
-
-    TryStringOut(str, outName, inOutNameSize, (BfpResult*)outResult);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFile_GetFullPath(const char* inPath, char* outPath, int* inOutPathSize, BfpFileResult* outResult)
-{
-    String str;
-
-    if (inPath[0] == '/')
-    {
-        str = inPath;
-    }
-    else
-    {
-        char* cwdPtr = getcwd(NULL, 0);
-        Beefy::String cwdPath = cwdPtr;
-        free(cwdPtr);
-        str = GetAbsPath(inPath, cwdPath);
-    }
-    TryStringOut(str, outPath, inOutPathSize, (BfpResult*)outResult);
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFile_GetActualPath(const char* inPath, char* outPath, int* inOutPathSize, BfpFileResult* outResult)
-{
-    NOT_IMPL;
-}
-
-// BfpFindFileData
-
-struct BfpFindFileData
-{
-    BfpFindFileFlags mFlags;    
-    DIR* mDirStruct;
-    Beefy::String mWildcard;
-    Beefy::String mDirPath;
-
-    dirent* mDirEnt;
-    bool mHasStat;
-    struct stat mStat;
-};
-
-BFP_EXPORT BfpFindFileData* BFP_CALLTYPE BfpFindFileData_FindFirstFile(const char* path, BfpFindFileFlags flags, BfpFileResult* outResult)
-{
-    Beefy::String findStr = path;
-    Beefy::String wildcard;
-    
-    int lastSlashPos = std::max((int)findStr.LastIndexOf('/'), (int)findStr.LastIndexOf('\\'));
-    if (lastSlashPos != -1)
-    {
-        wildcard = findStr.Substring(lastSlashPos + 1);
-        findStr = findStr.Substring(0, lastSlashPos);
-    }
-    if (wildcard == "*.*")
-        wildcard = "*";
-    
-    DIR* dir = opendir(findStr.c_str());
-    if (dir == NULL)
-    {
-        OUTRESULT(BfpFileResult_NotFound);
-        return NULL;
-    }
-
-    BfpFindFileData* findData = new BfpFindFileData();
-    findData->mFlags = flags;
-    findData->mDirPath = findStr;
-    findData->mDirStruct = dir;    
-    findData->mWildcard = wildcard;
-    findData->mHasStat = false;
-    findData->mDirEnt = NULL;
-        
-    if (!BfpFindFileData_FindNextFile(findData))
-    {            
-        OUTRESULT(BfpFileResult_NoResults);
-        delete findData;
-        return NULL;
-    }    
-
-    OUTRESULT(BfpFileResult_Ok);
-    return findData;
-}
-
-static void GetStat(BfpFindFileData* findData)
-{
-    if (findData->mHasStat)
-        return;
-
-    Beefy::String filePath = findData->mDirPath + "/" + findData->mDirEnt->d_name;
-    
-    findData->mStat = { 0 };
-    int result = stat(filePath.c_str(), &findData->mStat);
-
-    findData->mHasStat = true;
-}
-
-static bool BfpFindFileData_CheckFilter(BfpFindFileData* findData)
-{
-    bool isDir = false;
-    if (findData->mDirEnt->d_type == DT_DIR)
-        isDir = true;
-    if (findData->mDirEnt->d_type == DT_LNK)
-    {
-        GetStat(findData);
-        isDir = S_ISDIR(findData->mStat.st_mode);
-    }
-
-    if (isDir)
-    {
-        if ((findData->mFlags & BfpFindFileFlag_Directories) == 0)          
-            return false;
-        
-        if ((strcmp(findData->mDirEnt->d_name, ".") == 0) || (strcmp(findData->mDirEnt->d_name, "..") == 0))        
-            return false;        
-    }
-    else
-    {
-        if ((findData->mFlags & BfpFindFileFlag_Files) == 0)
-            return false;
-    }   
-
-    //TODO: Check actual wildcards.
-
-    return true;
-}
-
-BFP_EXPORT bool BFP_CALLTYPE BfpFindFileData_FindNextFile(BfpFindFileData* findData)
-{    
-    while (true)
-    {
-        findData->mHasStat = false;        
-        findData->mDirEnt = readdir(findData->mDirStruct);
-        if (findData->mDirEnt == NULL)
-            return false;
-
-        if (BfpFindFileData_CheckFilter(findData))
-            break;
-    }
-
-    return true;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFindFileData_GetFileName(BfpFindFileData* findData, char* outName, int* inOutNameSize, BfpFileResult* outResult)
-{
-    Beefy::String name = findData->mDirEnt->d_name;
-    TryStringOut(name, outName, inOutNameSize, (BfpResult*)outResult);
-}
-
-BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpFindFileData_GetTime_LastWrite(BfpFindFileData* findData)
-{
-    GetStat(findData);
-    return BfpToTimeStamp(findData->mStat.st_mtim);
-}
-
-BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpFindFileData_GetTime_Created(BfpFindFileData* findData)
-{
-    GetStat(findData);
-    return BfpToTimeStamp(findData->mStat.st_ctim);
-}
-
-BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpFindFileData_GetTime_Access(BfpFindFileData* findData)
-{
-    GetStat(findData);
-    return BfpToTimeStamp(findData->mStat.st_atim);
-}
-
-BFP_EXPORT BfpFileAttributes BFP_CALLTYPE BfpFindFileData_GetFileAttributes(BfpFindFileData* findData)
-{
-    BfpFileAttributes flags = BfpFileAttribute_None;
-    if (S_ISDIR(findData->mStat.st_mode))
-        flags = (BfpFileAttributes)(flags | BfpFileAttribute_Directory);
-    if (S_ISREG(findData->mStat.st_mode))
-        flags = (BfpFileAttributes)(flags | BfpFileAttribute_Normal);
-    else if (!S_ISLNK(findData->mStat.st_mode))
-        flags = (BfpFileAttributes)(flags | BfpFileAttribute_Device);
-    if ((findData->mStat.st_mode & S_IRUSR) == 0)
-        flags = (BfpFileAttributes)(flags | BfpFileAttribute_ReadOnly);
-    return flags;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpFindFileData_Release(BfpFindFileData* findData)
-{
-    delete findData;
-}
-
-BFP_EXPORT int BFP_CALLTYPE BfpStack_CaptureBackTrace(int framesToSkip, intptr* outFrames, int wantFrameCount)
-{
-    //
-    return 0;
-}
-
-BFP_EXPORT void BFP_CALLTYPE BfpOutput_DebugString(const char* str)
-{
-    fputs(str, stdout);
-    fflush(stdout);
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void Beefy::BFFatalError(const StringImpl& message, const StringImpl& file, int line)
-{
-	String error;
-    error += "ERROR: ";
-	error += message;
-	error += " in ";
-	error += file;
-	error += StrFormat(" line %d", line);
-	BfpSystem_FatalError(error.c_str(), "FATAL ERROR");
-}
+#include "../posix/PosixCommon.cpp"

+ 2 - 0
BeefySysLib/platform/osx/BFPlatform.h

@@ -6,6 +6,8 @@
 #include <string>
 
 #define BF_PLATFORM_MACOS
+#define BF_PLATFORM_POSIX
+#define BF_PLATFORM_DARWIN
 #define BF_PLATFORM_NAME "BF_PLATFORM_MACOS"
 
 #define BF_IMPORT extern "C"

+ 2341 - 0
BeefySysLib/platform/posix/PosixCommon.cpp

@@ -0,0 +1,2341 @@
+#include "Common.h"
+#include "BFPlatform.h"
+#include <sys/stat.h>
+#include <sys/sysinfo.h>
+#include <sys/wait.h>
+#include <wchar.h>
+#include <fcntl.h>
+#include <time.h>
+#include <link.h>
+#include <dirent.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <signal.h>
+#include <spawn.h>
+#include <dlfcn.h>
+#include "../PlatformInterface.h"
+#include "../PlatformHelper.h"
+#include "../util/CritSect.h"
+#include "../util/Dictionary.h"
+#include "../util/Hash.h"
+#ifdef BFP_HAS_EXECINFO
+#include <execinfo.h>
+#endif
+
+#ifdef BFP_HAS_BACKTRACE
+#include "backtrace.h"
+#include "backtrace-supported.h"
+#endif
+#include "../third_party/stb/stb_sprintf.h"
+#include <cxxabi.h>
+#include <random>
+
+#ifndef BFP_PRINTF
+#define BFP_PRINTF(...) printf (__VA_ARGS__)
+#define BFP_ERRPRINTF(...) fprintf (stderr, __VA_ARGS__)
+#endif
+
+USING_NS_BF;
+
+struct BfpPipeInfo
+{
+	String mPipePath;
+	int mWriteHandle;
+};
+
+struct BfpFile
+{	
+	BfpPipeInfo* mPipeInfo;
+	int mHandle;	
+	bool mNonBlocking;
+	bool mAllowTimeout;	
+	bool mIsStd;
+
+	BfpFile()
+	{
+		mPipeInfo = NULL;
+		mHandle = -1;		
+		mNonBlocking = false;
+		mAllowTimeout = false;		
+		mIsStd = false;
+	}
+
+	BfpFile(int handle)
+	{	
+		mPipeInfo = NULL;
+		mHandle = handle;		
+		mNonBlocking = false;
+		mAllowTimeout = false;		
+		mIsStd = false;
+	}
+
+	~BfpFile()
+	{
+		delete mPipeInfo;
+	}
+};
+
+BfpTimeStamp BfpToTimeStamp(const timespec& ts)
+{
+    return (int64)(ts.tv_sec * 10000000) + (int64)(ts.tv_nsec / 100) + 116444736000000000;
+}
+
+int gBFPlatformLastError = 0;
+
+uint32 Beefy::BFTickCount()
+{    
+    struct timespec now;
+    if (clock_gettime(CLOCK_MONOTONIC, &now))
+        return 0;
+    return (uint32)((uint64)now.tv_sec * 1000.0 + (uint64)now.tv_nsec / 1000000);
+}
+
+int64 Beefy::EndianSwap(int64 val)
+{
+	return __builtin_bswap64(val);
+}
+
+/*int* GetStdHandle(int32 handleId)
+{
+    if (handleId == STD_INPUT_HANDLE)
+        return (int*)STDIN_FILENO;
+    if (handleId == STD_OUTPUT_HANDLE)
+        return (int*)STDOUT_FILENO;
+    return (int*)STDERR_FILENO;
+}*/
+
+/*int32 GetFileType(HANDLE fileHandle)
+{
+    if (isatty(file->mHandleHandle))
+        return FILE_TYPE_CHAR;
+    return FILE_TYPE_DISK;
+}*/
+
+/*bool WriteFile(HANDLE hFile, void* lpBuffer, uint32 nNumberOfBytesToWrite, uint32* lpNumberOfBytesWritten, OVERLAPPED* lpOverlapped)
+{
+#ifdef BF_PLATFORM_IOS
+    int logType = -1;
+    if (hFile == (int*)STDOUT_FILENO)
+        logType = LOG_WARNING;
+    else if (hFile == (int*)STDERR_FILENO)
+        logType = LOG_ERR;
+    
+    if (logType != -1)
+    {
+        static std::string strOut;
+        strOut.resize(nNumberOfBytesToWrite);
+        memcpy(&strOut[0], lpBuffer, nNumberOfBytesToWrite);
+        if ((strOut[0] != '\r') && (strOut[0] != '\n'))
+            syslog(LOG_WARNING, "%s", strOut.c_str());
+    }
+#endif
+    
+    int writeCount = (int)::write((int)(intptr)hFile, lpBuffer, nNumberOfBytesToWrite);
+    if (writeCount == -1)
+    {
+        //TODO: set gBFPlatformLastError
+        lpNumberOfBytesWritten = 0;
+        return false;
+    }
+    
+    *lpNumberOfBytesWritten = (uint32)writeCount;
+    return true;
+}*/
+
+int64 Beefy::GetFileTimeWrite(const StringImpl& path)
+{
+    struct stat statbuf = {0};
+    int result = stat(path.c_str(), &statbuf);
+    if (result == -1)
+        return 0;
+    
+    //int64 fileTime = 0;
+    //BFSystemTimeToFileTime(statbuf.st_mtime, 0, &fileTime);
+    return statbuf.st_mtime;
+}
+
+/*DWORD GetTimeZoneInformation(TIME_ZONE_INFORMATION* lpTimeZoneInformation)
+{
+    std::wstring tzName0 = Beefy::UTF8Decode(tzname[0]);
+    std::wstring tzName1 = Beefy::UTF8Decode(tzname[1]);
+    
+    bool isDST = false;
+    
+    time_t timeNow;
+    time(&timeNow);
+    tm tmNow = *gmtime(&timeNow);
+    isDST = tmNow.tm_isdst;
+    
+    struct tm checkTM;
+    memset(&checkTM, 0, sizeof(tm));
+    checkTM.tm_mday = 1;
+    checkTM.tm_year = tmNow.tm_year;
+    time_t checkTime = mktime(&checkTM);
+    
+    time_t lastOffset = 0;
+    time_t minOffset = 0;
+    time_t maxOffset = 0;
+    
+    for (int pass = 0; pass < 2; pass++)
+    {
+        int searchDir = 60*60*24;
+        int thresholdCount = 0;
+        
+        while (true)
+        {
+            checkTime += searchDir;
+            
+            tm checkTM = *gmtime(&checkTime);
+            
+            if (checkTM.tm_year != tmNow.tm_year)
+                break; // No DST
+            
+            mktime(&checkTM);
+            
+            time_t offset = checkTM.tm_gmtoff;
+            if (lastOffset != offset)
+            {
+                if (thresholdCount == 0)
+                {
+                    minOffset = offset;
+                    maxOffset = offset;
+                }
+                else if (thresholdCount == 3)
+                {
+                    SYSTEMTIME* sysTimeP = (offset == minOffset) ?
+                    &lpTimeZoneInformation->StandardDate :
+                    &lpTimeZoneInformation->DaylightDate;
+                    
+                    if (offset == minOffset)
+                        tzName0 = Beefy::UTF8Decode(checkTM.tm_zone);
+                    else
+                        tzName1 = Beefy::UTF8Decode(checkTM.tm_zone);
+                    
+                    sysTimeP->wDay = 0;
+                    sysTimeP->wDayOfWeek = 0;
+                    sysTimeP->wYear = checkTM.tm_year + 1900;
+                    sysTimeP->wMonth = checkTM.tm_mon;
+                    sysTimeP->wDay = checkTM.tm_mday + 1;
+                    sysTimeP->wHour = checkTM.tm_hour;
+                    sysTimeP->wMinute = checkTM.tm_min;
+                    sysTimeP->wSecond = checkTM.tm_sec;
+                    sysTimeP->wMilliseconds = 0;
+                    
+                    break;
+                }
+                else
+                {
+                    if (thresholdCount == 1)
+                        searchDir /= -24;
+                    else
+                        searchDir /= -60;
+                    minOffset = std::min(minOffset, offset);
+                    maxOffset = std::max(maxOffset, offset);
+                }
+                thresholdCount++;
+                lastOffset = offset;
+            }
+        }
+    }
+	
+    wcsncpy(lpTimeZoneInformation->StandardName, tzName0.c_str(), 32);
+    wcsncpy(lpTimeZoneInformation->DaylightName, tzName1.c_str(), 32);
+    
+    lpTimeZoneInformation->DaylightBias = (int32)maxOffset;
+    lpTimeZoneInformation->StandardBias = (int32)minOffset;
+    
+    if (minOffset == maxOffset)
+        return 0;
+    return isDST ? 2 : 1;
+}*/
+
+bool Beefy::FileExists(const StringImpl& path, String* outActualName)
+{
+    struct stat statbuf = {0};
+    int result = stat(path.c_str(), &statbuf);
+    if (result != 0)
+        return false;
+    return !S_ISDIR(statbuf.st_mode);
+}
+
+bool Beefy::DirectoryExists(const StringImpl& path, String* outActualName)
+{
+    struct stat statbuf = {0};
+    int result = stat(path.c_str(), &statbuf);
+    if (result != 0)
+        return false;
+    return S_ISDIR(statbuf.st_mode);
+}
+
+uint64 Beefy::BFGetTickCountMicro()
+{
+    struct timespec now;
+    if (clock_gettime(CLOCK_MONOTONIC, &now))
+        return 0;
+    return ((uint64)now.tv_sec * 1000000.0 + (uint64)now.tv_nsec / 1000);
+}
+
+uint64 Beefy::BFGetTickCountMicroFast()
+{
+    return BFGetTickCountMicro();
+}
+
+/*
+int64 abs(int64 val)
+{
+    return llabs(val);
+}
+*/
+void mkdir(const char* path)
+{
+    mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
+}
+
+typedef void(*CrashInfoFunc)();
+
+static CritSect gSysCritSect;
+static String gCrashInfo;
+static Array<CrashInfoFunc> gCrashInfoFuncs;
+
+#ifdef BFP_HAS_BACKTRACE
+
+struct bt_ctx {
+    struct backtrace_state *state;
+    int error;
+};
+
+static void error_callback(void *data, const char *msg, int errnum)
+{
+    struct bt_ctx *ctx = (bt_ctx*)data;
+    BFP_ERRPRINTF("ERROR: %s (%d)", msg, errnum);
+    ctx->error = 1;
+}
+
+static void syminfo_callback (void *data, uintptr_t pc, const char *symname, uintptr_t symval, uintptr_t symsize)
+{
+    char str[4096];
+    if (symname)
+        stbsp_snprintf(str, 4096, "%@ %s\n", pc, symname);
+    else
+        stbsp_snprintf(str, 4096, "%@\n", pc);
+    BFP_ERRPRINTF("%s", str);
+}
+
+static int full_callback(void *data, uintptr_t pc, const char* filename, int lineno, const char* function)
+{
+    struct bt_ctx *ctx = (bt_ctx*)data;
+    if (function)
+    {
+        int status = -1;
+        char* demangledName = abi::__cxa_demangle(function, NULL, NULL, &status );
+        const char* showName = (demangledName != NULL) ? demangledName : function;
+
+        char str[4096];
+        stbsp_snprintf(str, 4096, "%@ %s %s:%d\n", pc, showName, filename?filename:"??", lineno);
+        BFP_ERRPRINTF("%s", str);
+
+        if (demangledName != NULL)
+            free(demangledName);
+    }
+    else
+        backtrace_syminfo (ctx->state, pc, syminfo_callback, error_callback, data);
+    return 0;
+}
+
+static int simple_callback(void *data, uintptr_t pc)
+{
+    struct bt_ctx *ctx = (bt_ctx*)data;
+    backtrace_pcinfo(ctx->state, pc, full_callback, error_callback, data);
+    return 0;
+}
+
+static inline void bt(struct backtrace_state *state)
+{
+    struct bt_ctx ctx = {state, 0};
+    //backtrace_print(state, 0, stdout);
+    backtrace_simple(state, 2, simple_callback, error_callback, &ctx);
+}
+
+#endif
+
+typedef void(*CrashInfoFunc)();
+
+static String gCmdLine;
+static String gExePath;
+
+typedef struct _Unwind_Context _Unwind_Context;   // opaque
+
+typedef enum {
+  _URC_NO_REASON = 0,
+  _URC_OK = 0,
+  _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
+  _URC_FATAL_PHASE2_ERROR = 2,
+  _URC_FATAL_PHASE1_ERROR = 3,
+  _URC_NORMAL_STOP = 4,
+  _URC_END_OF_STACK = 5,
+  _URC_HANDLER_FOUND = 6,
+  _URC_INSTALL_CONTEXT = 7,
+  _URC_CONTINUE_UNWIND = 8,
+  _URC_FAILURE = 9
+} _Unwind_Reason_Code;
+
+typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn)(struct _Unwind_Context *, void *);
+extern "C" _Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn, void *);
+extern "C" uintptr_t _Unwind_GetIP(struct _Unwind_Context *context);
+
+static String gUnwindExecStr;
+static int gUnwindIdx = 0;
+
+static _Unwind_Reason_Code UnwindHandler(struct _Unwind_Context* context, void* ref)
+{   
+    gUnwindIdx++;
+    if (gUnwindIdx < 2)
+        return _URC_NO_REASON;
+    
+    void* addr = (void*)_Unwind_GetIP(context);
+
+#if BFP_HAS_ATOS    
+    gUnwindExecStr += StrFormat(" %p", addr);
+#else
+    Dl_info info;
+    if (dladdr(addr, &info) && info.dli_sname)
+    {
+        BFP_ERRPRINTF("0x%p %s\n", addr, info.dli_sname);
+    }
+#endif
+    return _URC_NO_REASON;
+}
+
+static bool FancyBacktrace()
+{
+    gUnwindExecStr += StrFormat("atos -p %d", getpid());    
+    _Unwind_Backtrace(&UnwindHandler, NULL);
+#if BFP_HAS_ATOS
+    return system(gUnwindExecStr.c_str()) == 0;
+#else    
+    return true;
+#endif
+}
+
+static void Crashed()
+{
+    //
+    {
+        AutoCrit autoCrit(gSysCritSect);
+
+        String debugDump;
+
+        debugDump += "**** FATAL APPLICATION ERROR ****\n";
+
+        for (auto func : gCrashInfoFuncs)
+            func();
+
+        if (!gCrashInfo.IsEmpty())
+        {
+            debugDump += gCrashInfo;
+            debugDump += "\n";
+        }
+
+        BFP_ERRPRINTF("%s", debugDump.c_str());
+    }
+
+    if (!FancyBacktrace())
+    {
+#ifdef BFP_HAS_EXECINFO        
+        void* array[64];
+        size_t size;
+        char** strings;
+        size_t i;
+
+        size = backtrace(array, 64);
+        strings = backtrace_symbols(array, size);
+
+        for (i = 0; i < size; i++)
+            BFP_ERRPRINTF("%s\n", strings[i]);
+
+        free(strings);
+#endif
+    }
+
+    exit(1);
+}
+
+static void SigHandler(int sig)
+{
+	//printf("SigHandler paused...\n");	
+
+    const char* sigName = NULL;
+    switch (sig)
+    {
+    case SIGFPE:
+        sigName = "SIGFPE";
+        break;
+    case SIGSEGV:
+        sigName = "SIGSEGV";
+        break;
+    case SIGABRT:
+        sigName = "SIGABRT";
+        break;
+    case SIGILL:
+        sigName = "SIGILL";
+        break;
+    }
+
+    if (sigName != NULL)
+        gCrashInfo += StrFormat("Signal: %s\n", sigName);
+    else
+        gCrashInfo += StrFormat("Signal: %d\n", sig);
+    Crashed();
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpSystem_Init(int version, BfpSystemInitFlags flags)
+{
+    if (version != BFP_VERSION)
+    {
+        BfpSystem_FatalError(StrFormat("Bfp build version '%d' does not match requested version '%d'", BFP_VERSION, version).c_str(), "BFP FATAL ERROR");
+    }
+
+    signal(SIGSEGV, SigHandler);
+    signal(SIGFPE, SigHandler);
+    signal(SIGABRT, SigHandler);
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpSystem_SetCommandLine(int argc, char** argv)
+{    
+	char* relPath = argv[0];
+
+	char* cwd = getcwd(NULL, 0);
+	gExePath = GetAbsPath(relPath, cwd);
+	free(cwd);
+
+	for (int i = 0; i < argc; i++)
+	{
+		if (i != 0)
+			gCmdLine.Append(' ');
+
+		String arg = argv[i];
+		if ((arg.Contains(' ')) || (arg.Contains('\"')))
+		{
+			arg.Replace("\"", "\\\"");
+			gCmdLine.Append("\"");
+			gCmdLine.Append(arg);
+			gCmdLine.Append("\"");
+		}
+		else
+			gCmdLine.Append(arg);
+	}
+}
+
+
+BFP_EXPORT void BFP_CALLTYPE BfpSystem_SetCrashReportKind(BfpCrashReportKind crashReportKind)
+{
+
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpSystem_AddCrashInfoFunc(BfpCrashInfoFunc crashInfoFunc)
+{
+    AutoCrit autoCrit(gSysCritSect);
+    gCrashInfoFuncs.Add(crashInfoFunc);
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpSystem_AddCrashInfo(const char* str) // Can do at any time, or during CrashInfoFunc callbacks
+{
+    AutoCrit autoCrit(gSysCritSect);
+    gCrashInfo.Append(str);
+}
+
+void BfpSystem_Shutdown()
+{
+
+}
+
+BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_TickCount()
+{
+    return Beefy::BFTickCount();
+}
+
+BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpSystem_GetTimeStamp()
+{
+	struct timeval tv;
+	BfpTimeStamp result = 11644473600LL;
+	gettimeofday(&tv, NULL);
+	result += tv.tv_sec;
+	result *= 10000000LL;
+	result += tv.tv_usec * 10;
+	return result;
+}
+
+BFP_EXPORT uint16 BFP_CALLTYPE BfpSystem_EndianSwap16(uint16 val)
+{
+    return __builtin_bswap16(val);
+}
+
+BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_EndianSwap32(uint32 val)
+{
+    return __builtin_bswap32(val);
+}
+
+BFP_EXPORT uint64 BFP_CALLTYPE BfpSystem_EndianSwap64(uint64 val)
+{
+    return __builtin_bswap64(val);
+}
+
+BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_InterlockedExchange32(uint32* ptr, uint32 val)
+{
+	// __sync_lock_test_and_set only has Acquire semantics, so we need a __sync_synchronize to enforce a full barrier
+	uint32 prevVal = __sync_lock_test_and_set(ptr, val);
+	__sync_synchronize();
+	return prevVal;	
+}
+
+BFP_EXPORT uint64 BFP_CALLTYPE BfpSystem_InterlockedExchange64(uint64* ptr, uint64 val)
+{
+	// __sync_lock_test_and_set only has Acquire semantics, so we need a __sync_synchronize to enforce a full barrier
+	uint64 prevVal = __sync_lock_test_and_set(ptr, val);
+	__sync_synchronize();
+	return prevVal;	
+}
+
+BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_InterlockedExchangeAdd32(uint32* ptr, uint32 val)
+{
+	return __sync_fetch_and_add(ptr, val);
+}
+
+BFP_EXPORT uint64 BFP_CALLTYPE BfpSystem_InterlockedExchangeAdd64(uint64* ptr, uint64 val)
+{
+	return __sync_fetch_and_add(ptr, val);
+}
+
+BFP_EXPORT uint32 BFP_CALLTYPE BfpSystem_InterlockedCompareExchange32(uint32* ptr, uint32 oldVal, uint32 newVal)
+{
+    return __sync_val_compare_and_swap(ptr, oldVal, newVal);
+}
+
+BFP_EXPORT uint64 BFP_CALLTYPE BfpSystem_InterlockedCompareExchange64(uint64* ptr, uint64 oldVal, uint64 newVal)
+{
+    return __sync_val_compare_and_swap(ptr, oldVal, newVal);
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpSystem_FatalError(const char* error, const char* title)
+{
+    BFP_ERRPRINTF("%s\n", error);
+    fflush(stderr);
+    Crashed();
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpSystem_GetCommandLine(char* outStr, int* inOutStrSize, BfpSystemResult* outResult)
+{
+    TryStringOut(gCmdLine, outStr, inOutStrSize, (BfpResult*)outResult);
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpSystem_GetExecutablePath(char* outStr, int* inOutStrSize, BfpSystemResult* outResult)
+{
+#ifdef BF_PLATFORM_DARWIN
+    if (gExePath.IsEmpty())
+    {
+        char path[4096];
+        uint32_t size = sizeof(path);
+        if (_NSGetExecutablePath(path, &size) == 0)
+            gExePath = path;
+
+        // When when running with a './file', we end up with an annoying '/./' in our path
+        gExePath.Replace("/./", "/");        
+    }
+#endif
+
+	TryStringOut(gExePath, outStr, inOutStrSize, (BfpResult*)outResult);
+}
+
+extern char **environ;
+
+BFP_EXPORT void BFP_CALLTYPE BfpSystem_GetEnvironmentStrings(char* outStr, int* inOutStrSize, BfpSystemResult* outResult)
+{
+    String env;
+
+    char** envPtr = environ;
+    while (true)
+    {
+        char* envStr = *envPtr;
+        env.Append(envStr, strlen(envStr) + 1);
+        ++envPtr;
+    }
+
+    TryStringOut(env, outStr, inOutStrSize, (BfpResult*)outResult);
+}
+
+BFP_EXPORT int BFP_CALLTYPE BfpSystem_GetNumLogicalCPUs(BfpSystemResult* outResult)
+{	
+#ifdef BF_PLATFORM_ANDROID
+    //TODO: Handle this
+    OUTRESULT(BfpSystemResult_Ok);
+	return 1;
+#elif defined BF_PLATFORM_DARWIN
+    OUTRESULT(BfpSystemResult_Ok);
+    int count = 1;
+    size_t count_len = sizeof(count);
+    sysctlbyname("hw.logicalcpu", &count, &count_len, NULL, 0);
+    return count;
+#else
+    OUTRESULT(BfpSystemResult_Ok);
+    return get_nprocs_conf();
+#endif
+}
+
+BFP_EXPORT int64 BFP_CALLTYPE BfpSystem_GetCPUTick()
+{
+	return 10000000;
+}
+
+BFP_EXPORT int64 BFP_CALLTYPE BfpSystem_GetCPUTickFreq()
+{
+	struct timespec now;
+	clock_gettime(CLOCK_MONOTONIC, &now);
+	return (now.tv_sec * 10000000LL) + now.tv_nsec / 100;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpSystem_CreateGUID(BfpGUID* outGuid)
+{
+// 	uuid_t guid;	
+// 	uuid_generate(guid);	
+// 	BfpGUID bfpGuid;
+// 	memcpy(&bfpGuid, guid, 16);
+// 	return bfpGuid;
+
+	uint8* ptr = (uint8*)outGuid;
+
+	std::random_device rd;
+	std::mt19937 gen(rd());
+	std::uniform_int_distribution<uint8> dis(0, 255);
+	for (int i = 0; i < 16; i++)
+		ptr[i] = dis(gen);
+
+	// variant must be 10xxxxxx
+	ptr[8] &= 0xBF;
+	ptr[8] |= 0x80;
+
+	// version must be 0100xxxx
+	ptr[6] &= 0x4F;
+	ptr[6] |= 0x40;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpSystem_GetComputerName(char* outStr, int* inOutStrSize, BfpSystemResult* outResult)
+{
+	char hostName[1024];
+	gethostname(hostName, 1024);
+	TryStringOut(hostName, outStr, inOutStrSize, (BfpResult*)outResult);
+}
+
+// BfpProcess
+
+BFP_EXPORT intptr BFP_CALLTYPE BfpProcess_GetCurrentId()
+{
+    return getpid();
+}
+
+BFP_EXPORT bool BFP_CALLTYPE BfpProcess_IsRemoteMachine(const char* machineName)
+{
+    return false;
+}
+
+BFP_EXPORT BfpProcess* BFP_CALLTYPE BfpProcess_GetById(const char* machineName, int processId, BfpProcessResult* outResult)
+{
+    NOT_IMPL;
+    return NULL;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpProcess_Enumerate(const char* machineName, BfpProcess** outProcesses, int* inOutProcessesSize, BfpProcessResult* outResult)
+{
+    NOT_IMPL;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpProcess_Release(BfpProcess* process)
+{
+    NOT_IMPL;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpProcess_GetMainWindowTitle(BfpProcess* process, char* outTitle, int* inOutTitleSize, BfpProcessResult* outResult)
+{
+    NOT_IMPL;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpProcess_GetProcessName(BfpProcess* process, char* outName, int* inOutNameSize, BfpProcessResult* outResult)
+{
+    NOT_IMPL;
+}
+
+BFP_EXPORT int BFP_CALLTYPE BfpProcess_GetProcessId(BfpProcess* process)
+{
+    NOT_IMPL;
+    return 0;
+}
+
+// BfpSpawn
+
+struct BfpSpawn
+{
+    int mPid;
+    bool mExited;
+    int mStatus;
+    int mStdInFD;
+    int mStdOutFD;
+    int mStdErrFD;
+};
+
+BFP_EXPORT BfpSpawn* BFP_CALLTYPE BfpSpawn_Create(const char* inTargetPath, const char* args, const char* workingDir, const char* env, BfpSpawnFlags flags, BfpSpawnResult* outResult)
+{
+    Beefy::Array<Beefy::StringView> stringViews;
+
+    //printf("Executing: %s %s %x\n", inTargetPath, args, flags);
+
+	if ((workingDir != NULL) && (workingDir[0] != 0))
+	{
+		if (chdir(workingDir) != 0)
+		{
+			//printf("CHDIR failed %s\n", workingDir);
+			OUTRESULT(BfpSpawnResult_UnknownError);
+			return NULL;
+		}
+	}
+
+	String newArgs;
+	String tempFileName;
+
+	if ((flags & BfpSpawnFlag_UseArgsFile) != 0)
+	{
+		char tempFileNameStr[256];
+		int size = 256;
+		BfpFileResult fileResult;
+		BfpFile_GetTempFileName(tempFileNameStr, &size, &fileResult);
+		if (fileResult == BfpFileResult_Ok)
+		{
+			tempFileName = tempFileNameStr;
+
+			BfpFileResult fileResult;
+			BfpFile* file = BfpFile_Create(tempFileNameStr, BfpFileCreateKind_CreateAlways, BfpFileCreateFlag_Write, BfpFileAttribute_Normal, &fileResult);
+			if (file == NULL)
+			{
+				OUTRESULT(BfpSpawnResult_TempFileError);
+				return NULL;
+			}
+
+			if ((flags & BfpSpawnFlag_UseArgsFile_Native) != 0)
+			{
+				UTF16String wStr = UTF8Decode(args);
+
+				if ((flags & BfpSpawnFlag_UseArgsFile_BOM) != 0)
+				{
+					uint8 bom[2] = { 0xFF, 0xFE };
+					BfpFile_Write(file, bom, 2, -1, NULL);
+				}
+
+				BfpFile_Write(file, wStr.c_str(), wStr.length() * 2, -1, NULL);
+			}
+			else
+				BfpFile_Write(file, args, strlen(args), -1, NULL);
+			BfpFile_Release(file);
+
+			newArgs.Append("@");
+			newArgs.Append(tempFileName);
+			if (newArgs.Contains(' '))
+			{
+				newArgs.Insert(0, '\"');
+				newArgs.Append('\"');
+			}
+
+			args = newArgs.c_str();
+		}
+	}
+
+    int32 firstCharIdx = -1;
+    bool inQuote = false;
+
+	String targetPath = inTargetPath;
+	String verb;
+	if ((flags & BfpSpawnFlag_UseShellExecute) != 0)
+	{
+		String target = targetPath;
+		int barPos = (int)target.IndexOf('|');
+		if (barPos != -1)
+		{
+			verb = targetPath.Substring(barPos + 1);
+			targetPath.RemoveToEnd(barPos);
+		}				
+	}
+
+    int32 i = 0;
+    for ( ; true; i++)
+    {
+        char c = args[i];
+        if (c == '\0')
+            break;
+        if ((c == ' ') && (!inQuote))
+        {
+            if (firstCharIdx != -1)
+            {
+                stringViews.Add(Beefy::StringView(args, firstCharIdx, i - firstCharIdx));
+                firstCharIdx = -1;
+            }
+        }
+        else
+        {
+            if (firstCharIdx == -1)
+                firstCharIdx = i;
+            if (c == '"')
+                inQuote = !inQuote;
+            else if ((inQuote) && (c == '\\'))
+            {
+                c = args[i + 1];
+                if (c == '"')
+                    i++;
+            }
+        }
+    }
+    if (firstCharIdx != -1)
+        stringViews.Add(Beefy::StringView(args, firstCharIdx, i - firstCharIdx));
+
+    Beefy::Array<char*> argvArr;
+
+    if ((flags & BfpSpawnFlag_ArgsIncludesTarget) == 0)
+        argvArr.Add(strdup(targetPath.c_str()));
+
+    for (int32 i = 0; i < (int32)stringViews.size(); i++)
+    {
+        Beefy::StringView stringView = stringViews[i];
+        char* str = NULL;
+        for (int32 pass = 0; pass < 2; pass++)
+        {
+            char* strPtr = str;
+
+            int32 strPos = 0;
+            for (int32 char8Idx = 0; char8Idx < stringView.mLength; char8Idx++)
+            {
+                char c = stringView.mPtr[char8Idx];
+                if (c == '"')
+                    inQuote = !inQuote;
+                else
+                {
+                    if ((inQuote) && (c == '\\') && (char8Idx < stringView.mLength - 1))
+                    {
+                        char nextC = stringView.mPtr[char8Idx + 1];
+                        if (nextC == '"')
+                        {
+                            c = nextC;
+                            char8Idx++;
+                        }
+                    }
+                    if (strPtr != NULL)
+                        *(strPtr++) = c;
+                    strPos++;
+                }
+            }
+            if (pass == 0)
+                str = (char*)malloc(strPos + 1);
+            else
+                *(strPtr++) = 0;
+        }
+
+        argvArr.Add(str);
+    }
+    argvArr.Add(NULL);
+
+    char** argv = NULL;
+
+    //pid_t pid = 0;
+    //int status = posix_spawn(&pid, targetPath, NULL, NULL, &argvArr[0], environ);
+
+    Beefy::Array<char*> envArr;
+    if (env != NULL)
+    {
+        char* envPtr = (char*)env;
+        while (true)
+        {
+            if (*envPtr == 0)
+                break;
+
+            envArr.Add(envPtr);
+            envPtr += strlen(envPtr) + 1;
+        }
+    }
+    envArr.Add(NULL);
+
+    int stdInFD[2];
+    int stdOutFD[2];
+    int stdErrFD[2];
+
+	bool failed = false;
+	if ((flags & BfpSpawnFlag_RedirectStdInput) != 0)
+		if (pipe(stdInFD) != 0)
+			failed = true;
+	if ((flags & BfpSpawnFlag_RedirectStdOutput) != 0)
+		if (pipe(stdOutFD) != 0)
+			failed = true;
+	if ((flags & BfpSpawnFlag_RedirectStdError) != 0)
+		if (pipe(stdErrFD) != 0)
+			failed = true;
+	if (failed)
+	{
+		//printf("Pipe failed\n");
+		OUTRESULT(BfpSpawnResult_UnknownError);
+		return NULL;
+	}
+
+    BfpSpawn* spawn;
+    pid_t pid = fork();
+    if (pid == -1) // Error
+    {
+        OUTRESULT(BfpSpawnResult_UnknownError);
+        return NULL;
+    }
+    else if (pid == 0) // Child
+    {
+        if ((flags & BfpSpawnFlag_RedirectStdInput) != 0)
+            while ((dup2(stdInFD[0], STDIN_FILENO) == -1) && (errno == EINTR)) {}
+        if ((flags & BfpSpawnFlag_RedirectStdOutput) != 0)
+            while ((dup2(stdOutFD[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {}
+        if ((flags & BfpSpawnFlag_RedirectStdError) != 0)
+            while ((dup2(stdErrFD[1], STDERR_FILENO) == -1) && (errno == EINTR)) {}
+
+        // If successful then this shouldn't return at all:
+        int result;
+
+        if (env != NULL)
+            result = execve(targetPath.c_str(), (char* const*)&argvArr[0], (char* const*)&envArr[0]);
+        else
+            result = execv(targetPath.c_str(), (char* const*)&argvArr[0]);
+
+        BFP_ERRPRINTF("Couldn't execute %s\n", targetPath.c_str());
+
+        exit(-1);
+    }
+    else // Parent
+    {
+        spawn = new BfpSpawn();
+
+        if ((flags & BfpSpawnFlag_RedirectStdInput) != 0)
+        {
+            spawn->mStdInFD = stdInFD[1];
+            close(stdInFD[0]);
+        }
+        else
+            spawn->mStdInFD = 0;
+
+        if ((flags & BfpSpawnFlag_RedirectStdOutput) != 0)
+        {
+            spawn->mStdOutFD = stdOutFD[0];
+            close(stdOutFD[1]);
+        }
+        else
+            spawn->mStdOutFD = 0;
+
+        if ((flags & BfpSpawnFlag_RedirectStdError) != 0)
+        {
+            spawn->mStdErrFD = stdErrFD[0];
+            close(stdErrFD[1]);
+        }
+        else
+            spawn->mStdErrFD = 0;
+    }
+
+    for (auto val : argvArr)
+        free(val);
+
+    //printf("Spawn pid:%d status:%d\n", pid, status);
+    spawn->mPid = pid;
+    spawn->mExited = false;
+    spawn->mStatus = 0;
+
+    return spawn;
+}
+
+void BfpSpawn_Release(BfpSpawn* spawn)
+{
+    // We don't support 'detaching' currently- this can create zombie processes since we
+    //  don't have a reaper strategy
+    BfpSpawn_WaitFor(spawn, -1, NULL, NULL);
+
+    delete spawn;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpSpawn_GetStdHandles(BfpSpawn* spawn, BfpFile** outStdIn, BfpFile** outStdOut, BfpFile** outStdErr)
+{
+    if (outStdIn != NULL)
+    {
+        *outStdIn = new BfpFile(spawn->mStdInFD);
+        spawn->mStdInFD = 0;
+    }
+
+    if (outStdOut != NULL)
+    {
+        *outStdOut = new BfpFile(spawn->mStdOutFD);
+        spawn->mStdOutFD = 0;
+    }
+
+    if (outStdErr != NULL)
+    {
+        *outStdErr = new BfpFile(spawn->mStdErrFD);
+        spawn->mStdErrFD = 0;
+    }
+}
+
+bool BfpSpawn_WaitFor(BfpSpawn* spawn, int waitMS, int* outExitCode, BfpSpawnResult* outResult)
+{
+    OUTRESULT(BfpSpawnResult_Ok);
+    if (!spawn->mExited)
+    {
+        int flags = 0;
+        if (waitMS != -1)
+        {
+            flags = WNOHANG;
+        }
+        //TODO: Implement values other than 0 or -1 for waitMS?
+
+        pid_t result = waitpid(spawn->mPid, &spawn->mStatus, flags);
+        if (result != spawn->mPid)
+            return false;
+
+        spawn->mExited = true;
+    }
+
+    if (!WIFEXITED(spawn->mStatus) && !WIFSIGNALED(spawn->mStatus))
+        return false;
+
+    if (outExitCode != NULL)
+        *outExitCode = WEXITSTATUS(spawn->mStatus);
+    return true;
+}
+
+// BfpFileWatcher
+
+BFP_EXPORT BfpFileWatcher* BFP_CALLTYPE BfpFileWatcher_WatchDirectory(const char* path, BfpDirectoryChangeFunc callback, BfpFileWatcherFlags flags, void* userData, BfpFileResult* outResult)
+{
+    NOT_IMPL;
+    return NULL;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpFileWatcher_Release(BfpFileWatcher* fileWatcher)
+{
+    NOT_IMPL;
+}
+
+// BfpThread
+
+struct BfpThread
+{
+    bool mPThreadReleased;
+    BfpThreadStartProc mStartProc;
+    void* mThreadParam;
+#ifndef BFP_HAS_PTHREAD_TIMEDJOIN_NP
+    BfpEvent* mDoneEvent;
+#endif    
+
+    pthread_t mPThread;
+    int mRefCount;
+    int mPriority;   
+
+    BfpThread()
+    {
+    }
+
+    void Release()
+    {
+        int refCount = __sync_fetch_and_sub(&mRefCount, 1) - 1;
+        if (refCount == 0)
+            delete this;
+    }
+};
+
+struct BfpThreadInfo
+{
+    intptr mStackBase;
+    int mStackLimit;
+};
+
+static __thread BfpThread* gCurrentThread;
+static __thread BfpThreadInfo gCurrentThreadInfo;
+
+void* ThreadFunc(void* threadParam)
+{
+    BfpThread* thread = (BfpThread*)threadParam;
+    gCurrentThread = thread;
+    thread->mStartProc(thread->mThreadParam);
+#ifndef BFP_HAS_PTHREAD_TIMEDJOIN_NP
+    BfpEvent_Set(thread->mDoneEvent, true);
+#endif      
+    thread->Release();
+    return NULL;
+}
+
+BFP_EXPORT BfpThread* BFP_CALLTYPE BfpThread_Create(BfpThreadStartProc startProc, void* threadParam, intptr stackSize, BfpThreadCreateFlags flags, BfpThreadId* outThreadId)
+{
+    BfpThread* thread = new BfpThread();
+    thread->mPThreadReleased = false;
+    thread->mStartProc = startProc;
+    thread->mThreadParam = threadParam;
+    thread->mRefCount = 2;
+    thread->mPriority = 0;
+#ifndef BFP_HAS_PTHREAD_TIMEDJOIN_NP
+    thread->mDoneEvent = BfpEvent_Create(BfpEventFlag_None);
+#endif    
+
+    BF_ASSERT(sizeof(pthread_t) <= sizeof(void*));    
+    pthread_attr_t params;
+    pthread_attr_init(&params);
+    pthread_attr_setstacksize(&params, stackSize);
+    //pthread_attr_setdetachstate(&params,PTHREAD_CREATE_DETACHED);
+
+    pthread_create(&thread->mPThread, &params, ThreadFunc, (void*)thread);
+
+    pthread_attr_destroy(&params);
+    
+    if (outThreadId != NULL)
+        *outThreadId = (BfpThreadId)thread->mPThread;
+
+    return thread;
+}
+
+#define FIXTHREAD() \
+    pthread_t pt; \
+    if (((intptr)thread & 1) != 0) \
+    { \
+        pt = (pthread_t)((intptr)thread & ~3); \
+        thread = NULL; \
+    } else \
+    pt = thread->mPThread
+
+BFP_EXPORT void BFP_CALLTYPE BfpThread_Release(BfpThread* thread)
+{
+    FIXTHREAD();
+    if (thread == NULL)
+        return;
+
+#ifndef BFP_HAS_PTHREAD_TIMEDJOIN_NP
+    BfpEvent_Release(thread->mDoneEvent);
+#endif
+    if (!thread->mPThreadReleased)
+    {
+        pthread_detach(thread->mPThread);
+        thread->mPThreadReleased = true;
+    }
+    thread->Release();
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpThread_SetName(BfpThread* thread, const char* name, BfpThreadResult* outResult)
+{
+	OUTRESULT(BfpThreadResult_Ok);
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpThread_GetName(BfpThread* thread, char* outName, int* inOutNameSize, BfpThreadResult* outResult)
+{
+	String str = "";
+	TryStringOut(str, outName, inOutNameSize, (BfpResult*)outResult);
+}
+
+BFP_EXPORT BfpThread* BFP_CALLTYPE BfpThread_GetCurrent()
+{
+    if (gCurrentThread == NULL)
+    {
+        // Not a "true" BfpThread, this is either the main thread or a thread we didn't create
+        return (BfpThread*)((intptr)pthread_self() | 1);
+    }
+    return gCurrentThread;
+}
+
+BFP_EXPORT BfpThreadId BFP_CALLTYPE BfpThread_GetCurrentId()
+{
+    if (gCurrentThread == NULL)
+    {
+        return (BfpThreadId)((intptr)pthread_self());
+    }
+    return (BfpThreadId)gCurrentThread->mPThread;
+}
+
+BFP_EXPORT bool BFP_CALLTYPE BfpThread_WaitFor(BfpThread* thread, int waitMS)
+{
+    FIXTHREAD();
+
+    if (waitMS == -1)
+    {
+        pthread_join(pt, NULL);
+        thread->mPThreadReleased = true;
+        return true;
+    }
+
+#ifdef BFP_HAS_PTHREAD_TIMEDJOIN_NP
+    struct timespec waitTime;
+    waitTime.tv_sec = waitMS / 1000;
+    waitTime.tv_nsec = (waitMS % 1000) * 1000000;
+    int result = pthread_timedjoin_np(pt, NULL, &waitTime);
+    if (result == 0)
+    {
+        if (thread != NULL)
+            thread->mPThreadReleased = true;
+        return true;
+    }
+    return false;
+#else
+    if (thread == NULL)
+        BF_FATAL("Invalid thread with non-infinite wait");
+    return BfpEvent_WaitFor(thread->mDoneEvent, waitMS);    
+#endif
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpSpawn_Kill(BfpSpawn* spawn, int exitCode, BfpKillFlags killFlags, BfpSpawnResult* outResult)
+{
+	//TODO: Implement
+	OUTRESULT(BfpSpawnResult_UnknownError);
+}
+
+BFP_EXPORT BfpThreadPriority BFP_CALLTYPE BfpThread_GetPriority(BfpThread* thread, BfpThreadResult* outResult)
+{
+    FIXTHREAD();
+
+    OUTRESULT(BfpThreadResult_Ok);
+    if (thread == NULL)
+        return (BfpThreadPriority)0;
+
+    return (BfpThreadPriority)thread->mPriority;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpThread_SetPriority(BfpThread* thread, BfpThreadPriority threadPriority, BfpThreadResult* outResult)
+{
+    // In effect, we have two 'nice' settings: 0 (normal) or 10 (low)
+    //  High-priority settings just don't do anything
+    //pid_t tid = syscall(SYS_gettid);
+    //int ret = setpriority(PRIO_PROCESS, tid, -std::min(nPriority, 0) * 10);
+    OUTRESULT(BfpThreadResult_Ok);
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpThread_Suspend(BfpThread* thread, BfpThreadResult* outResult)
+{
+    NOT_IMPL;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpThread_Resume(BfpThread* thread, BfpThreadResult* outResult)
+{
+    NOT_IMPL;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpThread_GetIntRegisters(BfpThread* thread, intptr* outStackPtr, intptr* outIntRegs, int* inOutIntRegCount, BfpThreadResult* outResult)
+{
+    NOT_IMPL;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpThread_GetStackInfo(BfpThread* thread, intptr* outStackBase, int* outStackLimit, BfpThreadResult* outResult)
+{
+#ifdef BFP_HAS_PTHREAD_GETATTR_NP
+    if (gCurrentThreadInfo.mStackBase == 0)
+    {
+        void* stackBase = 0;
+        size_t stackLimit = 0;
+
+        pthread_attr_t attr;
+        pthread_getattr_np(pthread_self(), &attr);
+        pthread_attr_getstack(&attr, &stackBase, &stackLimit);
+
+        gCurrentThreadInfo.mStackBase = (intptr)stackBase + stackLimit;
+        gCurrentThreadInfo.mStackLimit = (int)stackLimit;
+        pthread_attr_destroy(&attr);
+    }
+
+    *outStackBase = gCurrentThreadInfo.mStackBase;
+    *outStackLimit = gCurrentThreadInfo.mStackLimit;
+
+	OUTRESULT(BfpThreadResult_Ok);
+#else
+    OUTRESULT(BfpThreadResult_UnknownError);
+#endif
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpThread_Sleep(int sleepMS)
+{
+    usleep(sleepMS * 1000);
+}
+
+BFP_EXPORT bool BFP_CALLTYPE BfpThread_Yield()
+{
+    return sched_yield() == 0;
+}
+
+struct BfpCritSect
+{
+    pthread_mutex_t mPMutex;
+};
+
+BFP_EXPORT BfpCritSect* BFP_CALLTYPE BfpCritSect_Create()
+{
+    BfpCritSect* critSect = new BfpCritSect();
+
+    pthread_mutexattr_t     attributes;        
+    pthread_mutexattr_init(&attributes);
+    pthread_mutexattr_settype(&attributes, PTHREAD_MUTEX_RECURSIVE);
+    pthread_mutex_init(&critSect->mPMutex, &attributes);
+    pthread_mutexattr_destroy(&attributes);
+
+    return critSect;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpCritSect_Release(BfpCritSect* critSect)
+{
+    pthread_mutex_destroy(&critSect->mPMutex);
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpCritSect_Enter(BfpCritSect* critSect)
+{
+    pthread_mutex_lock(&critSect->mPMutex);
+}
+
+BFP_EXPORT bool BFP_CALLTYPE BfpCritSect_TryEnter(BfpCritSect* critSect, int waitMS)
+{
+    if (waitMS == -1)
+    {
+        BfpCritSect_Enter(critSect);
+        return true;
+    }
+    else if (waitMS == 0)
+    {
+        return pthread_mutex_trylock(&critSect->mPMutex) == 0;
+    }
+    
+    uint32 start = Beefy::BFTickCount();
+    while ((int)(Beefy::BFTickCount() - start) < waitMS)
+    {
+        if (pthread_mutex_trylock(&critSect->mPMutex) == 0)
+        {                
+            return true;
+        }
+    }
+    return false;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpCritSect_Leave(BfpCritSect* critSect)
+{
+    pthread_mutex_unlock(&critSect->mPMutex);
+}
+
+BFP_EXPORT BfpTLS* BFP_CALLTYPE BfpTLS_Create()
+{
+    pthread_key_t key = 0;
+    pthread_key_create(&key, NULL);
+    return (BfpTLS*)(intptr)key;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpTLS_Release(BfpTLS* tls)
+{
+    pthread_key_delete((pthread_key_t)(intptr)tls);
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpTLS_SetValue(BfpTLS* tls, void* value)
+{
+    pthread_setspecific((pthread_key_t)(intptr)tls, value);
+}
+
+BFP_EXPORT void* BFP_CALLTYPE BfpTLS_GetValue(BfpTLS* tls)
+{
+    return pthread_getspecific((pthread_key_t)(intptr)tls);
+}
+
+struct BfpEvent
+{
+    pthread_mutex_t mMutex;
+    pthread_cond_t mCondVariable;
+    bool mSet;
+    bool mManualReset;
+};
+
+BFP_EXPORT BfpEvent* BFP_CALLTYPE BfpEvent_Create(BfpEventFlags flags)
+{
+    BfpEvent* event = new BfpEvent();
+    pthread_mutex_init(&event->mMutex, NULL);
+    pthread_cond_init(&event->mCondVariable, NULL);
+    event->mSet = (flags & (BfpEventFlag_InitiallySet_Auto | BfpEventFlag_InitiallySet_Manual)) != 0;
+    event->mManualReset = (flags & BfpEventFlag_InitiallySet_Manual) != 0;
+    return event;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpEvent_Release(BfpEvent* event)
+{
+    pthread_cond_destroy(&event->mCondVariable);
+    pthread_mutex_destroy(&event->mMutex);
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpEvent_Set(BfpEvent* event, bool requireManualReset)
+{
+    pthread_mutex_lock(&event->mMutex);
+    event->mSet = true;
+    if (requireManualReset)
+        event->mManualReset = true;
+    if (event->mManualReset)
+        pthread_cond_broadcast(&event->mCondVariable);
+    else
+        pthread_cond_signal(&event->mCondVariable);
+    pthread_mutex_unlock(&event->mMutex);
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpEvent_Reset(BfpEvent* event, BfpEventResult* outResult)
+{
+    event->mSet = false;
+    event->mManualReset = false;
+}
+
+BFP_EXPORT bool BFP_CALLTYPE BfpEvent_WaitFor(BfpEvent* event, int waitMS)
+{
+    int result = pthread_mutex_lock(&event->mMutex);
+    BF_ASSERT(result == 0);
+    while (!event->mSet)
+    {
+        if (waitMS == -1)
+        {
+            pthread_cond_wait(&event->mCondVariable, &event->mMutex);
+        }
+        else
+        {
+            timespec ts;
+            clock_gettime(CLOCK_REALTIME, &ts);
+            ts.tv_sec += waitMS / 1000;
+            ts.tv_nsec += (waitMS % 1000) * 1000000;
+            
+            result = pthread_cond_timedwait(&event->mCondVariable, &event->mMutex, &ts);
+            
+            if (waitMS == (uint32)-1)
+                BF_ASSERT(result == 0);
+            
+            if (result != 0)
+            {
+                // Timeout                
+                pthread_mutex_unlock(&event->mMutex);
+                return false;
+            }
+        }
+    }
+    if (!event->mManualReset)
+        event->mSet = false;        
+    pthread_mutex_unlock(&event->mMutex);
+    return true;
+}
+
+BFP_EXPORT BfpDynLib* BFP_CALLTYPE BfpDynLib_Load(const char* fileName)
+{
+    BfpDynLib* mod = NULL;
+    
+    static const char* prefixes[] = {NULL, "lib"};
+    static const char* suffixes[] = {NULL, ".so", ".dylib"};
+    
+    for (int prefixIdx = 0; prefixIdx < sizeof(prefixes)/sizeof(prefixes[0]); prefixIdx++)
+    {
+        for (int suffixIdx = 0; suffixIdx < sizeof(suffixes)/sizeof(suffixes[0]); suffixIdx++)
+        {
+            const char* prefix = prefixes[prefixIdx];
+            const char* suffix = suffixes[suffixIdx];
+            
+            Beefy::String checkName = fileName;
+            if (prefix != NULL)
+                checkName = Beefy::String(prefix) + checkName;
+            if (suffix != NULL)
+            {
+                int dotPos = checkName.LastIndexOf('.');
+                if (dotPos != -1)
+                    checkName.RemoveToEnd(dotPos);
+                checkName += suffix;
+            }
+            
+            mod = (BfpDynLib*)dlopen(checkName.c_str(), RTLD_LAZY);
+            if (mod != NULL)
+                return mod;
+        }
+    }
+    
+     /*mod = (BfpDynLib*)dlopen("/var/Beef/qt-build/Debug/bin/libIDEHelper.so", RTLD_LAZY);;
+     if (mod == NULL)
+     {
+         printf("Err: %s\n", dlerror());
+         fflush(stdout);
+     }*/
+
+    return NULL;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpDynLib_Release(BfpDynLib* lib)
+{
+    dlclose((void*)lib);
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpDynLib_GetFilePath(BfpDynLib* lib, char* outPath, int* inOutPathSize, BfpLibResult* outResult)
+{
+    Beefy::String path;
+
+#ifdef BFP_HAS_DLINFO
+    link_map* linkMap = NULL;
+    dlinfo((void*)lib, RTLD_DI_LINKMAP, &linkMap);
+    if (linkMap == NULL)
+    {
+        OUTRESULT(BfpLibResult_UnknownError);
+        return;
+    }
+
+    path = linkMap->l_name;    
+#else
+    Dl_info info;    
+    if (dladdr((void*)lib, &info) == 0)
+    {
+        OUTRESULT(BfpLibResult_UnknownError);
+        return;
+    }
+
+    path = info.dli_fname;    
+#endif
+
+    TryStringOut(path, outPath, inOutPathSize, (BfpResult*)outResult);
+}
+
+BFP_EXPORT void* BFP_CALLTYPE BfpDynLib_GetProcAddress(BfpDynLib* lib, const char* name)
+{
+    return dlsym((void*)lib, name);
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpDirectory_Create(const char* path, BfpFileResult* outResult)
+{
+    if (mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0)
+    {
+        switch (errno)
+        {
+        case EEXIST:
+            OUTRESULT(BfpFileResult_AlreadyExists);
+            break;
+        case ENOENT:
+            OUTRESULT(BfpFileResult_NotFound);
+            break;
+        default:
+            OUTRESULT(BfpFileResult_UnknownError);
+            break;
+        }
+    }
+    else    
+        OUTRESULT(BfpFileResult_Ok);
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpDirectory_Rename(const char* oldName, const char* newName, BfpFileResult* outResult)
+{
+    NOT_IMPL;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpDirectory_Delete(const char* path, BfpFileResult* outResult)
+{
+    if (rmdir(path) != 0)
+    {
+        switch (errno)
+        {
+        case ENOENT:
+            OUTRESULT(BfpFileResult_NotFound);
+            break;
+        default:
+            OUTRESULT(BfpFileResult_UnknownError);
+            break;
+        }
+    }
+    else    
+        OUTRESULT(BfpFileResult_Ok);
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpDirectory_GetCurrent(char* outPath, int* inOutPathSize, BfpFileResult* outResult)
+{
+    char* str = getcwd(NULL, 0);
+    Beefy::String path = str;
+    free(str);
+    TryStringOut(path, outPath, inOutPathSize, (BfpResult*)outResult);
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpDirectory_SetCurrent(const char* path, BfpFileResult* outResult)
+{    
+    if (chdir(path) != 0)
+        OUTRESULT(BfpFileResult_NotFound);  
+    else    
+        OUTRESULT(BfpFileResult_Ok);
+}
+
+BFP_EXPORT bool BFP_CALLTYPE BfpDirectory_Exists(const char* path)
+{
+    struct stat statbuf = {0};
+    int result = stat(path, &statbuf);
+    if (result != 0)
+        return false;
+    return S_ISDIR(statbuf.st_mode);   
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpDirectory_GetSysDirectory(BfpSysDirectoryKind sysDirKind, char* outPath, int* inOutPathLen, BfpFileResult* outResult)
+{
+	String path = "~";
+	TryStringOut(path, outPath, inOutPathLen, (BfpResult*)outResult);
+}
+
+BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_Create(const char* inName, BfpFileCreateKind createKind, BfpFileCreateFlags createFlags, BfpFileAttributes createdFileAttrs, BfpFileResult* outResult)
+{    
+	auto _DoCreate = [&](String& name)
+	{
+		int flags = 0;
+		int mode = 0;
+		int pipePairHandle = -1;
+
+		if ((createFlags & (BfpFileCreateFlag_Read | BfpFileCreateFlag_Write)) == (BfpFileCreateFlag_Read | BfpFileCreateFlag_Write))
+			flags |= O_RDWR;
+		else if ((createFlags & BfpFileCreateFlag_Read) != 0)
+			flags |= O_RDONLY;
+		else if ((createFlags & BfpFileCreateFlag_Write) != 0)
+			flags |= O_WRONLY;
+
+		if ((createFlags & BfpFileCreateFlag_Append) != 0)
+			flags |= O_APPEND;
+		if ((createFlags & BfpFileCreateFlag_Truncate) != 0)
+			flags |= O_TRUNC;
+		if ((createFlags & (BfpFileCreateFlag_NonBlocking | BfpFileCreateFlag_AllowTimeouts)) != 0)
+			flags |= O_NONBLOCK;
+
+		if ((createFlags & BfpFileCreateFlag_Pipe) != 0)
+		{
+			name = "/tmp/" + name;
+			if ((createKind == BfpFileCreateKind_CreateAlways) ||
+				(createKind == BfpFileCreateKind_CreateIfNotExists))
+			{
+				for (int pass = 0; pass < 2; pass++)
+				{
+					int result = mknod(name.c_str(), S_IFIFO | 0666, 0);
+					if (result == 0)
+						break;
+
+					int err = errno;
+					if (err == EEXIST)
+					{
+						err = remove(name.c_str());
+						if (err == 0)
+							continue;
+						OUTRESULT(BfpFileResult_AlreadyExists);
+						return -1;
+					}
+
+					OUTRESULT(BfpFileResult_UnknownError);
+					return -1;
+				}
+			}
+		}
+		else
+		{
+			if (createKind == BfpFileCreateKind_CreateAlways)
+				flags |= O_CREAT;
+			else if (createKind == BfpFileCreateKind_CreateIfNotExists)
+				flags |= O_CREAT | O_EXCL;
+		}
+
+		mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+
+		int result = open(name.c_str(), flags, mode);
+        //printf("BfpFile_Create %s %d %d %d\n", name.c_str(), result, flags, mode);
+
+		if (result <= 0)
+		{
+			switch (errno)
+			{
+			case EEXIST:
+				OUTRESULT(BfpFileResult_AlreadyExists);
+				break;
+			case ENOENT:
+				OUTRESULT(BfpFileResult_NotFound);
+				break;
+			case EACCES:
+				OUTRESULT(BfpFileResult_AccessError);
+				break;
+			default:
+				OUTRESULT(BfpFileResult_UnknownError);
+				break;
+			}
+			return -1;
+		}
+        return result;
+	};
+
+	BfpFile* bfpFile = NULL;
+
+	int result;	
+	if ((createFlags & BfpFileCreateFlag_Pipe) != 0)
+	{
+		int readHandle;
+		int writeHandle;
+
+		String name = inName;
+		String altName = name + "__";
+
+		bool isCreating = false;
+		if ((createKind == BfpFileCreateKind_CreateAlways) ||
+			(createKind == BfpFileCreateKind_CreateIfNotExists))
+		{
+			readHandle = _DoCreate(name);
+			writeHandle = _DoCreate(altName);
+			isCreating = true;
+		}
+		else
+		{
+			readHandle = _DoCreate(altName);
+			writeHandle = _DoCreate(name);
+		}
+
+		if ((readHandle != -1) && (writeHandle != -1))
+		{
+			OUTRESULT(BfpFileResult_Ok);
+
+			BfpPipeInfo* pipeInfo = new BfpPipeInfo();
+			pipeInfo->mWriteHandle = writeHandle;		
+			if (isCreating)
+				pipeInfo->mPipePath = name;
+			bfpFile = new BfpFile();
+			bfpFile->mHandle = readHandle;			
+			bfpFile->mPipeInfo = pipeInfo;
+		}
+		else
+		{
+			if (readHandle != -1)
+				close(readHandle);
+			if (writeHandle != -1)
+				close(writeHandle);
+
+			return NULL;
+		}
+	}
+	else
+	{
+		String name = inName;
+		int handle = _DoCreate(name);
+		if (handle == -1)
+			return NULL;
+
+		OUTRESULT(BfpFileResult_Ok);
+		bfpFile = new BfpFile();
+		bfpFile->mHandle = handle;
+	}
+
+	OUTRESULT(BfpFileResult_Ok);	
+	if ((createFlags & (BfpFileCreateFlag_NonBlocking | BfpFileCreateFlag_AllowTimeouts)) != 0)
+        bfpFile->mNonBlocking = true;
+	if ((createFlags & BfpFileCreateFlag_AllowTimeouts) != 0)       
+        bfpFile->mAllowTimeout = true;	
+    return bfpFile;
+}
+
+BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_GetStd(BfpFileStdKind kind, BfpFileResult* outResult)
+{
+	int h = -1;
+	switch (kind)
+	{
+	case BfpFileStdKind_StdOut:
+		h = STDOUT_FILENO;
+		break;
+	case BfpFileStdKind_StdError:
+		h = STDERR_FILENO;
+		break;
+	case BfpFileStdKind_StdIn:
+		h = STDIN_FILENO;
+		break;
+	}
+	if (h == -1)
+	{
+		OUTRESULT(BfpFileResult_NotFound);
+		return NULL;
+	}
+
+	BfpFile* bfpFile = new BfpFile();
+	bfpFile->mHandle = h;
+	bfpFile->mIsStd = true;
+
+	return bfpFile;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpFile_Release(BfpFile* file)
+{	
+	if ((file->mHandle != -1) && (!file->mIsStd))
+		close(file->mHandle);
+	if (file->mPipeInfo != NULL)
+	{
+		if (file->mPipeInfo->mWriteHandle != -1)
+			close(file->mPipeInfo->mWriteHandle);
+		
+		if (!file->mPipeInfo->mPipePath.IsEmpty())
+		{
+			int worked = remove(file->mPipeInfo->mPipePath.c_str());
+			remove((file->mPipeInfo->mPipePath + "__").c_str());
+			//printf("Removing %s %d\n", file->mPipeInfo->mPipePath.c_str(), worked);
+		}
+	}	
+
+	delete file;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpFile_Close(BfpFile* file, BfpFileResult* outResult)
+{
+	if (file->mHandle != -1)
+	{
+		close(file->mHandle);
+		file->mHandle = -1;
+		if (file->mPipeInfo != NULL)
+		{
+			close(file->mPipeInfo->mWriteHandle);
+			file->mPipeInfo->mWriteHandle = -1;
+		}
+
+		OUTRESULT(BfpFileResult_Ok);
+	}
+	else
+		OUTRESULT(BfpFileResult_UnknownError);
+}
+
+BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Write(BfpFile* file, const void* buffer, intptr size, int timeoutMS, BfpFileResult* outResult)
+{   
+	int writeHandle = file->mHandle;
+	if (file->mPipeInfo != NULL)
+		writeHandle = file->mPipeInfo->mWriteHandle;
+
+    intptr writeCount = ::write(writeHandle, buffer, size);
+// 	if ((writeCount > 0) && (file->mIsPipe))
+// 	{
+// 		::fsync(file->mHandle);
+// 	}
+
+    if (writeCount < 0)
+        OUTRESULT(BfpFileResult_UnknownError);
+    else if (writeCount != size)
+        OUTRESULT(BfpFileResult_PartialData);
+    else
+        OUTRESULT(BfpFileResult_Ok);
+    return writeCount;
+}
+
+BFP_EXPORT intptr BFP_CALLTYPE BfpFile_Read(BfpFile* file, void* buffer, intptr size, int timeoutMS, BfpFileResult* outResult)
+{
+	if (file->mNonBlocking)
+	{
+		if (!file->mAllowTimeout)		
+			timeoutMS = -1;		
+		
+		timeval timeout;
+		timeout.tv_sec = 0;
+		timeout.tv_usec = timeoutMS * 1000;
+
+		fd_set readFDSet;
+		FD_ZERO(&readFDSet);
+		FD_SET(file->mHandle, &readFDSet);
+
+		fd_set errorFDSet;
+		FD_ZERO(&errorFDSet);
+		FD_SET(file->mHandle, &errorFDSet);
+
+		if (select(file->mHandle + 1, &readFDSet, NULL, &errorFDSet, (timeoutMS == -1) ? NULL : &timeout) < 0)
+		{
+			OUTRESULT(BfpFileResult_Timeout);
+			return 0;
+		}
+	}
+
+    intptr readCount = ::read(file->mHandle, buffer, size);
+    if (readCount < 0)
+        OUTRESULT(BfpFileResult_UnknownError);
+    else if (readCount != size)
+        OUTRESULT(BfpFileResult_PartialData);
+    else
+        OUTRESULT(BfpFileResult_Ok);
+    return readCount;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpFile_Flush(BfpFile* file)
+{
+    ::fsync(file->mHandle);
+}
+
+BFP_EXPORT int64 BFP_CALLTYPE BfpFile_GetFileSize(BfpFile* file)
+{
+    int64 oldPos = (int64)lseek64(file->mHandle, 0, SEEK_CUR);
+    int64 size = (int64)lseek64(file->mHandle, 0, SEEK_END);
+    lseek64(file->mHandle, oldPos, SEEK_SET);
+    return (int64)size;
+}
+
+BFP_EXPORT int64 BFP_CALLTYPE BfpFile_Seek(BfpFile* file, int64 offset, BfpFileSeekKind seekKind)
+{
+    int whence;
+    if (seekKind == BfpFileSeekKind_Absolute)        
+        whence = SEEK_SET;
+    else if (seekKind == BfpFileSeekKind_Relative)
+        whence = SEEK_CUR;
+    else
+        whence = SEEK_END;
+    return lseek64(file->mHandle, offset, whence);
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpFile_Truncate(BfpFile* file)
+{
+    int64 curPos = (int64)lseek64(file->mHandle, 0, SEEK_CUR);
+	if (ftruncate64(file->mHandle, curPos) != 0)
+	{
+		//TODO: Report error?
+	}
+}
+
+BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpFile_GetTime_LastWrite(const char* path)
+{
+    struct stat statbuf = {0};
+    int result = stat(path, &statbuf);
+    if (result != 0)
+        return 0;    
+    return statbuf.st_mtime;
+}
+
+BFP_EXPORT BfpFileAttributes BFP_CALLTYPE BfpFile_GetAttributes(const char* path, BfpFileResult* outResult)
+{
+    NOT_IMPL;
+    return (BfpFileAttributes)0;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpFile_SetAttributes(const char* path, BfpFileAttributes attribs, BfpFileResult* outResult)
+{
+    NOT_IMPL;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpFile_Copy(const char* oldPath, const char* newPath, BfpFileCopyKind copyKind, BfpFileResult* outResult)
+{
+    int fd_to, fd_from;
+    char buf[4096];
+    ssize_t nread;  
+
+    fd_from = open(oldPath, O_RDONLY);
+    if (fd_from < 0)
+    {
+        OUTRESULT(BfpFileResult_NotFound);
+        return;
+    }
+
+    int flags = O_WRONLY | O_CREAT;
+    if (copyKind == BfpFileCopyKind_IfNotExists)
+        flags |= O_EXCL;
+
+    fd_to = open(newPath, flags, 0666);
+    if (fd_to < 0)
+    {
+        if (errno == EEXIST)
+        {
+            OUTRESULT(BfpFileResult_AlreadyExists);
+            goto out_error;
+        }
+
+        OUTRESULT(BfpFileResult_UnknownError);
+        goto out_error;
+    }
+
+    while (nread = read(fd_from, buf, sizeof buf), nread > 0)
+    {
+        char *out_ptr = buf;
+        ssize_t nwritten;
+
+        do {
+            nwritten = write(fd_to, out_ptr, nread);
+
+            if (nwritten >= 0)
+            {
+                nread -= nwritten;
+                out_ptr += nwritten;
+            }
+            else if (errno != EINTR)
+            {
+                OUTRESULT(BfpFileResult_UnknownError);
+                goto out_error;
+            }
+        } while (nread > 0);
+    }
+
+    if (nread == 0)
+    {
+        if (close(fd_to) < 0)
+        {
+            fd_to = -1;
+            OUTRESULT(BfpFileResult_UnknownError);
+            goto out_error;
+        }
+        close(fd_from);
+
+        /* Success! */
+        OUTRESULT(BfpFileResult_Ok);
+        return;
+    }
+
+out_error:
+    close(fd_from);
+    if (fd_to >= 0)
+        close(fd_to);   
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpFile_Rename(const char* oldPath, const char* newPath, BfpFileResult* outResult)
+{
+    NOT_IMPL;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpFile_Delete(const char* path, BfpFileResult* outResult)
+{
+    if (remove(path) != 0)
+    {
+        switch (errno)
+        {
+        case ENOENT:
+            OUTRESULT(BfpFileResult_NotFound);
+            break;
+        default:
+            OUTRESULT(BfpFileResult_UnknownError);
+            break;
+        }
+    }
+    else    
+        OUTRESULT(BfpFileResult_Ok);
+}
+
+BFP_EXPORT bool BFP_CALLTYPE BfpFile_Exists(const char* path)
+{
+    struct stat statbuf = {0};
+    int result = stat(path, &statbuf);
+    if (result != 0)
+        return false;
+    return !S_ISDIR(statbuf.st_mode);
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpFile_GetTempPath(char* outPath, int* inOutPathSize, BfpFileResult* outResult)
+{
+    NOT_IMPL;
+}
+
+static const char cHash64bToChar[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
+    'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F',
+    'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
+    'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_' };
+static void HashEncode64(StringImpl& outStr, uint64 val)
+{
+    for (int i = 0; i < 10; i++)
+    {
+        int charIdx = (int)((val >> (i * 6)) & 0x3F) - 1;
+        if (charIdx != -1)
+            outStr.Append(cHash64bToChar[charIdx]);
+    }
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpFile_GetTempFileName(char* outName, int* inOutNameSize, BfpFileResult* outResult)
+{
+    static uint32 uniqueIdx = 0;
+    BfpSystem_InterlockedExchangeAdd32(&uniqueIdx, 1);
+
+    Beefy::HashContext ctx;
+    ctx.Mixin(uniqueIdx);
+    ctx.Mixin(getpid());
+    ctx.Mixin(Beefy::BFGetTickCountMicro());
+
+    uint64 hash = ctx.Finish64();
+
+    String str = "/tmp/bftmp_";
+    HashEncode64(str, hash);
+
+    TryStringOut(str, outName, inOutNameSize, (BfpResult*)outResult);
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpFile_GetFullPath(const char* inPath, char* outPath, int* inOutPathSize, BfpFileResult* outResult)
+{
+    String str;
+
+    if (inPath[0] == '/')
+    {
+        str = inPath;
+    }
+    else
+    {
+        char* cwdPtr = getcwd(NULL, 0);
+        Beefy::String cwdPath = cwdPtr;
+        free(cwdPtr);
+        str = GetAbsPath(inPath, cwdPath);
+    }
+    TryStringOut(str, outPath, inOutPathSize, (BfpResult*)outResult);
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpFile_GetActualPath(const char* inPath, char* outPath, int* inOutPathSize, BfpFileResult* outResult)
+{
+    NOT_IMPL;
+}
+
+// BfpFindFileData
+
+struct BfpFindFileData
+{
+    BfpFindFileFlags mFlags;    
+    DIR* mDirStruct;
+    Beefy::String mWildcard;
+    Beefy::String mDirPath;
+
+    dirent* mDirEnt;
+    bool mHasStat;
+    struct stat mStat;
+};
+
+BFP_EXPORT BfpFindFileData* BFP_CALLTYPE BfpFindFileData_FindFirstFile(const char* path, BfpFindFileFlags flags, BfpFileResult* outResult)
+{
+    Beefy::String findStr = path;
+    Beefy::String wildcard;
+    
+    int lastSlashPos = std::max((int)findStr.LastIndexOf('/'), (int)findStr.LastIndexOf('\\'));
+    if (lastSlashPos != -1)
+    {
+        wildcard = findStr.Substring(lastSlashPos + 1);
+        findStr = findStr.Substring(0, lastSlashPos);
+    }
+    if (wildcard == "*.*")
+        wildcard = "*";
+    
+    DIR* dir = opendir(findStr.c_str());
+    if (dir == NULL)
+    {
+        OUTRESULT(BfpFileResult_NotFound);
+        return NULL;
+    }
+
+    BfpFindFileData* findData = new BfpFindFileData();
+    findData->mFlags = flags;
+    findData->mDirPath = findStr;
+    findData->mDirStruct = dir;    
+    findData->mWildcard = wildcard;
+    findData->mHasStat = false;
+    findData->mDirEnt = NULL;
+        
+    if (!BfpFindFileData_FindNextFile(findData))
+    {            
+        OUTRESULT(BfpFileResult_NoResults);
+        delete findData;
+        return NULL;
+    }    
+
+    OUTRESULT(BfpFileResult_Ok);
+    return findData;
+}
+
+static void GetStat(BfpFindFileData* findData)
+{
+    if (findData->mHasStat)
+        return;
+
+    Beefy::String filePath = findData->mDirPath + "/" + findData->mDirEnt->d_name;
+    
+    findData->mStat = { 0 };
+    int result = stat(filePath.c_str(), &findData->mStat);
+
+    findData->mHasStat = true;
+}
+
+static bool BfpFindFileData_CheckFilter(BfpFindFileData* findData)
+{
+    bool isDir = false;
+    if (findData->mDirEnt->d_type == DT_DIR)
+        isDir = true;
+    if (findData->mDirEnt->d_type == DT_LNK)
+    {
+        GetStat(findData);
+        isDir = S_ISDIR(findData->mStat.st_mode);
+    }
+
+    if (isDir)
+    {
+        if ((findData->mFlags & BfpFindFileFlag_Directories) == 0)          
+            return false;
+        
+        if ((strcmp(findData->mDirEnt->d_name, ".") == 0) || (strcmp(findData->mDirEnt->d_name, "..") == 0))        
+            return false;        
+    }
+    else
+    {
+        if ((findData->mFlags & BfpFindFileFlag_Files) == 0)
+            return false;
+    }   
+
+    //TODO: Check actual wildcards.
+
+    return true;
+}
+
+BFP_EXPORT bool BFP_CALLTYPE BfpFindFileData_FindNextFile(BfpFindFileData* findData)
+{    
+    while (true)
+    {
+        findData->mHasStat = false;        
+        findData->mDirEnt = readdir(findData->mDirStruct);
+        if (findData->mDirEnt == NULL)
+            return false;
+
+        if (BfpFindFileData_CheckFilter(findData))
+            break;
+    }
+
+    return true;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpFindFileData_GetFileName(BfpFindFileData* findData, char* outName, int* inOutNameSize, BfpFileResult* outResult)
+{
+    Beefy::String name = findData->mDirEnt->d_name;
+    TryStringOut(name, outName, inOutNameSize, (BfpResult*)outResult);
+}
+
+BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpFindFileData_GetTime_LastWrite(BfpFindFileData* findData)
+{
+    GetStat(findData);
+    return BfpToTimeStamp(findData->mStat.st_mtim);
+}
+
+BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpFindFileData_GetTime_Created(BfpFindFileData* findData)
+{
+    GetStat(findData);
+    return BfpToTimeStamp(findData->mStat.st_ctim);
+}
+
+BFP_EXPORT BfpTimeStamp BFP_CALLTYPE BfpFindFileData_GetTime_Access(BfpFindFileData* findData)
+{
+    GetStat(findData);
+    return BfpToTimeStamp(findData->mStat.st_atim);
+}
+
+BFP_EXPORT BfpFileAttributes BFP_CALLTYPE BfpFindFileData_GetFileAttributes(BfpFindFileData* findData)
+{
+    BfpFileAttributes flags = BfpFileAttribute_None;
+    if (S_ISDIR(findData->mStat.st_mode))
+        flags = (BfpFileAttributes)(flags | BfpFileAttribute_Directory);
+    if (S_ISREG(findData->mStat.st_mode))
+        flags = (BfpFileAttributes)(flags | BfpFileAttribute_Normal);
+    else if (!S_ISLNK(findData->mStat.st_mode))
+        flags = (BfpFileAttributes)(flags | BfpFileAttribute_Device);
+    if ((findData->mStat.st_mode & S_IRUSR) == 0)
+        flags = (BfpFileAttributes)(flags | BfpFileAttribute_ReadOnly);
+    return flags;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpFindFileData_Release(BfpFindFileData* findData)
+{
+    delete findData;
+}
+
+BFP_EXPORT int BFP_CALLTYPE BfpStack_CaptureBackTrace(int framesToSkip, intptr* outFrames, int wantFrameCount)
+{
+    //
+    return 0;
+}
+
+BFP_EXPORT void BFP_CALLTYPE BfpOutput_DebugString(const char* str)
+{
+    BFP_PRINTF("%s", str);
+    fflush(stdout);
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void Beefy::BFFatalError(const StringImpl& message, const StringImpl& file, int line)
+{
+	String error;
+    error += "ERROR: ";
+	error += message;
+	error += " in ";
+	error += file;
+	error += StrFormat(" line %d", line);
+	BfpSystem_FatalError(error.c_str(), "FATAL ERROR");
+}

+ 3 - 5
BeefySysLib/util/BeefPerf.cpp

@@ -21,16 +21,14 @@ typedef fd_set FD_SET;
 #include <sys/socket.h>
 #include <mach/error.h>
 #include <mach/mach.h>
-#include <cerrno>
-//#define errno (*__error())
 #endif
 
-//#include <rpcdce.h>
+#ifdef BF_PLATFORM_POSIX
+#include <cerrno>
+#endif
 
 #pragma comment(lib,"wsock32.lib")
 
-//#include "Hash.h"
-
 #pragma warning(disable:4996)
 
 USING_NS_BF;

+ 8 - 0
BeefySysLib/util/Hash.cpp

@@ -1356,7 +1356,9 @@ static int gDbgVizIdx = 0;
 
 HashContext::~HashContext()
 {
+#ifdef BF_PLATFORM_WINDOWS
 	delete mDbgVizStream;
+#endif
 }
 
 void HashContext::Reset()
@@ -1378,6 +1380,7 @@ void HashContext::Mixin(const void* data, int size)
 	{
 		int addBytes = std::min(size, 1024 - mBufSize);
 
+#ifdef BF_PLATFORM_WINDOWS
 		if (mDbgViz)
 		{
 			int findIdx = 0x2cc159;
@@ -1386,6 +1389,7 @@ void HashContext::Mixin(const void* data, int size)
 				NOP;
 			}
 		}
+#endif
 
 		memcpy(&mBuf[mBufSize], data, addBytes);
 		mBufSize += addBytes;
@@ -1417,6 +1421,7 @@ void HashContext::MixinStr(const StringImpl& str)
 
 Val128 HashContext::Finish128()
 {
+#ifdef BF_PLATFORM_WINDOWS
 	if (mDbgViz)
 	{
 // 		String dbg = "HashContext Dbg";
@@ -1441,6 +1446,7 @@ Val128 HashContext::Finish128()
 		if ((mDbgVizStream != NULL) && (mDbgVizStream->IsOpen()))
 			mDbgVizStream->Write(mBuf, mBufSize);
 	}
+#endif
 	
 	if (mBufSize <= 16)
 	{
@@ -1459,6 +1465,7 @@ Val128 HashContext::Finish128()
 
 uint64 HashContext::Finish64()
 {
+#ifdef BF_PLATFORM_WINDOWS
 	if (mDbgViz)
 	{
 		// 		String dbg = "HashContext Dbg";
@@ -1476,6 +1483,7 @@ uint64 HashContext::Finish64()
 		}
 		mDbgVizStream->Write(mBuf, mBufSize);
 	}
+#endif
 
 	if (mBufSize <= 8)
 	{

+ 4 - 0
BeefySysLib/util/Hash.h

@@ -109,16 +109,20 @@ public:
 	uint8 mBuf[1024];
 	int mBufSize;
 	int mBufOffset;
+#ifdef BF_PLATFORM_WINDOWS
 	bool mDbgViz;
 	FileStream* mDbgVizStream;
+#endif
 
 public:
 	HashContext()
 	{
 		mBufOffset = 0;
 		mBufSize = 0;
+#ifdef BF_PLATFORM_WINDOWS
 		mDbgViz = false;
 		mDbgVizStream = NULL;
+#endif
 	}
 
 	~HashContext();

+ 88 - 1
IDE/src/BuildContext.bf

@@ -157,9 +157,91 @@ namespace IDE
 			return didCommands ? .HadCommands : .NoCommands;
 		}
 
+		bool QueueProjectGNUArchive(Project project, String targetPath, Workspace.Options workspaceOptions, Project.Options options, String objectsArg)
+		{
+#if BF_PLATFORM_WINDOWS
+			String llvmDir = scope String(IDEApp.sApp.mInstallDir);
+			IDEUtils.FixFilePath(llvmDir);
+			llvmDir.Append("llvm/");
+#else
+		    String llvmDir = "";
+#endif
+
+		    //String error = scope String();
+
+		    TestManager.ProjectInfo testProjectInfo = null;
+			if (gApp.mTestManager != null)
+				testProjectInfo = gApp.mTestManager.GetProjectInfo(project);
+
+			bool isExe = (project.mGeneralOptions.mTargetType != Project.TargetType.BeefLib) || (testProjectInfo != null);
+			if (!isExe)
+				return true;
+			
+		    String arCmds = scope String(""); //-O2 -Rpass=inline 
+															 //(doClangCPP ? "-lc++abi " : "") +
+
+		    arCmds.AppendF("CREATE {}\n", targetPath);
+
+			for (let obj in objectsArg.Split(' '))
+			{
+				if (!obj.IsEmpty)
+				{
+					arCmds.AppendF("ADDMOD {}\n", obj);
+				}
+			}
+			arCmds.AppendF("SAVE\n");
+
+		    if (project.mNeedsTargetRebuild)
+		    {
+		        if (File.Delete(targetPath) case .Err)
+				{
+				    gApp.OutputLine("Failed to delete {0}", targetPath);
+				    return false;
+				}
+
+				String arPath = scope .();
+#if BF_PLATFORM_WINDOWS
+				arPath.Clear();
+				arPath.Append(gApp.mInstallDir);
+				arPath.Append(@"llvm\bin\llvm-ar.exe");
+#else
+				arPath.Append("/usr/bin/ar");
+#endif
+
+				String workingDir = scope String();
+				workingDir.Append(gApp.mInstallDir);
+
+				String scriptPath = scope .();
+				if (Path.GetTempFileName(scriptPath) case .Err)
+				{
+					return false;
+				}
+				if (File.WriteAllText(scriptPath, arCmds) case .Err)
+				{
+					gApp.OutputLine("Failed to write archive script {0}", scriptPath);
+					return false;
+				}
+
+				String cmdLine = scope .();
+				cmdLine.AppendF("-M");
+
+		        var runCmd = gApp.QueueRun(arPath, cmdLine, workingDir, .UTF8);
+		        runCmd.mOnlyIfNotFailed = true;
+				runCmd.mStdInData = new .(arCmds);
+		        var tagetCompletedCmd = new IDEApp.TargetCompletedCmd(project);
+		        tagetCompletedCmd.mOnlyIfNotFailed = true;
+		        gApp.mExecutionQueue.Add(tagetCompletedCmd);
+
+				project.mLastDidBuild = true;
+		    }
+
+			return true;
+		}
+
 		bool QueueProjectGNULink(Project project, String targetPath, Workspace.Options workspaceOptions, Project.Options options, String objectsArg)
 		{
 			bool isDebug = gApp.mConfigName.IndexOf("Debug", true) != -1;
+			
 
 #if BF_PLATFORM_WINDOWS
 			String llvmDir = scope String(IDEApp.sApp.mInstallDir);
@@ -1070,7 +1152,12 @@ namespace IDE
 
 			if (workspaceOptions.mToolsetType == .GNU)
 			{
-				if (!QueueProjectGNULink(project, targetPath, workspaceOptions, options, objectsArg))
+				if ((options.mBuildOptions.mBuildKind == .StaticLib) || (options.mBuildOptions.mBuildKind == .DynamicLib))
+				{
+					if (!QueueProjectGNUArchive(project, targetPath, workspaceOptions, options, objectsArg))
+						return false;
+				}
+				else if (!QueueProjectGNULink(project, targetPath, workspaceOptions, options, objectsArg))
 					return false;
 			}
 			else // MS

+ 19 - 0
IDE/src/BuildOptions.bf

@@ -45,6 +45,25 @@ namespace IDE
 				return (this != .Og) && (this != .OgPlus) && (this != .O0);
 			}
 		}
+
+		public enum RelocType
+		{
+			NotSet,
+			Static, 
+			PIC, 
+			DynamicNoPIC,
+			ROPI,
+			RWPI, 
+			ROPI_RWPI
+		}
+
+		public enum PICLevel
+		{
+			NotSet,
+			Not,
+			Small, 
+			Big
+		}
 	}
 
 	public class DistinctBuildOptions

+ 5 - 1
IDE/src/Compiler/BfCompiler.bf

@@ -7,6 +7,7 @@ using System.Diagnostics;
 using Beefy.widgets;
 using Beefy;
 using Beefy.utils;
+using IDE.Util;
 
 namespace IDE.Compiler
 {
@@ -465,7 +466,10 @@ namespace IDE.Compiler
 
 			var options = IDEApp.sApp.GetCurWorkspaceOptions();
 			String targetTriple = scope .();
-			Workspace.PlatformType.GetTargetTripleByName(gApp.mPlatformName, options.mToolsetType, targetTriple);
+			if (TargetTriple.IsTargetTriple(gApp.mPlatformName))
+				targetTriple.Set(gApp.mPlatformName);
+			else
+				Workspace.PlatformType.GetTargetTripleByName(gApp.mPlatformName, options.mToolsetType, targetTriple);
 
 			bool enableObjectDebugFlags = options.mEnableObjectDebugFlags;
 			bool emitObjectAccessCheck = options.mEmitObjectAccessCheck && enableObjectDebugFlags;

+ 4 - 3
IDE/src/Compiler/BfProject.bf

@@ -34,7 +34,7 @@ namespace IDE.Compiler
 
         [StdCall, CLink]
         extern static void BfProject_SetOptions(void* nativeBfProject, int32 targetType, char8* startupObject, char8* preprocessorMacros,
-            int32 optLevel, int32 ltoType, Flags flags);
+            int32 optLevel, int32 ltoType, int32 relocType, int32 picLevel, Flags flags);
 
         public void* mNativeBfProject;
         public bool mDisabled;
@@ -61,7 +61,8 @@ namespace IDE.Compiler
         }
 
         public void SetOptions(Project.TargetType targetType, String startupObject, List<String> preprocessorMacros,
-            BuildOptions.BfOptimizationLevel optLevel, BuildOptions.LTOType ltoType, bool mergeFunctions, bool combineLoads, bool vectorizeLoops, bool vectorizeSLP)
+            BuildOptions.BfOptimizationLevel optLevel, BuildOptions.LTOType ltoType, BuildOptions.RelocType relocType, BuildOptions.PICLevel picLevel,
+			bool mergeFunctions, bool combineLoads, bool vectorizeLoops, bool vectorizeSLP)
         {
 			Flags flags = default;
 			void SetFlags(bool val, Flags flag)
@@ -77,7 +78,7 @@ namespace IDE.Compiler
             String macrosStr = scope String();
             macrosStr.Join("\n", preprocessorMacros.GetEnumerator());
             BfProject_SetOptions(mNativeBfProject, (int32)targetType, startupObject, macrosStr, 
-                (int32)optLevel, (int32)ltoType, flags);
+                (int32)optLevel, (int32)ltoType, (int32)relocType, (int32)picLevel, flags);
         }
 
     }

+ 92 - 19
IDE/src/IDEApp.bf

@@ -420,6 +420,7 @@ namespace IDE
             public ArgsFileKind mUseArgsFile;
             public int32 mParallelGroup = -1;
 			public bool mIsTargetRun;
+			public String mStdInData ~ delete _;
         }
         public List<ExecutionCmd> mExecutionQueue = new List<ExecutionCmd>() ~ DeleteContainerAndItems!(_);
 
@@ -435,9 +436,11 @@ namespace IDE
             public Task<String> mErrorTask /*~ delete _*/;
             public Action<Task<String>> mOnErrorTaskComplete /*~ delete _*/;
 			public Monitor mMonitor = new Monitor() ~ delete _;
+			public String mStdInData ~ delete _;
 
 			public Thread mOutputThread;
 			public Thread mErrorThread;
+			public Thread mInputThread;
 
 			public int? mExitCode;
 			public bool mAutoDelete = true;
@@ -450,12 +453,17 @@ namespace IDE
 				/*if (mProcess != null)
 					mProcess.Close();*/
 
+				if (mInputThread != null)
+					mInputThread.Join();
+				delete mInputThread;
+
 				if (mOutputThread != null)
 					mOutputThread.Join();
 				delete mOutputThread;
+
 				if (mErrorThread != null)
 					mErrorThread.Join();
-				delete mErrorThread;				
+				delete mErrorThread;
 
 				delete mReadTask;
 				delete mOnReadTaskComplete;
@@ -7092,6 +7100,26 @@ namespace IDE
             }
         }
 
+		static void WriteInputThread(Object obj)
+		{
+			ExecutionInstance executionInstance = (ExecutionInstance)obj;
+
+			FileStream fileStream = scope FileStream();
+			if (executionInstance.mProcess.AttachStandardInput(fileStream) case .Err)
+				return;
+			
+			while (!executionInstance.mStdInData.IsEmpty)
+			{
+				switch (fileStream.TryWrite(.((.)executionInstance.mStdInData.Ptr, executionInstance.mStdInData.Length)))
+				{
+				case .Ok(int len):
+					executionInstance.mStdInData.Remove(0, len);
+				case .Err:
+					break;
+				}
+			}
+		}
+
 		static void ReadOutputThread(Object obj)
 		{
 			ExecutionInstance executionInstance = (ExecutionInstance)obj;
@@ -7131,7 +7159,7 @@ namespace IDE
 			}
 		}
 
-        public ExecutionInstance DoRun(String inFileName, String args, String workingDir, ArgsFileKind useArgsFile, Dictionary<String, String> envVars = null)
+        public ExecutionInstance DoRun(String inFileName, String args, String workingDir, ArgsFileKind useArgsFile, Dictionary<String, String> envVars = null, String stdInData = null)
         {
 			//Debug.Assert(executionInstance == null);
 
@@ -7146,6 +7174,8 @@ namespace IDE
             startInfo.SetArguments(args);
             startInfo.RedirectStandardOutput = true;
             startInfo.RedirectStandardError = true;
+			if (stdInData != null)
+				startInfo.RedirectStandardInput = true;
             startInfo.CreateNoWindow = true;
 			if (envVars != null)
 			{
@@ -7224,6 +7254,13 @@ namespace IDE
 			executionInstance.mErrorThread = new Thread(new => ReadErrorThread);
 			executionInstance.mErrorThread.Start(executionInstance, false);
 
+			if (stdInData != null)
+			{
+				executionInstance.mStdInData = new String(stdInData);
+				executionInstance.mInputThread = new Thread(new => WriteInputThread);
+				executionInstance.mInputThread.Start(executionInstance, false);
+			}
+
             return executionInstance;
         }
 
@@ -7502,7 +7539,7 @@ namespace IDE
                 else if (next is ExecutionQueueCmd)
                 {
                     var executionQueueCmd = (ExecutionQueueCmd)next;
-                    var executionInstance = DoRun(executionQueueCmd.mFileName, executionQueueCmd.mArgs, executionQueueCmd.mWorkingDir, executionQueueCmd.mUseArgsFile, executionQueueCmd.mEnvVars);
+                    var executionInstance = DoRun(executionQueueCmd.mFileName, executionQueueCmd.mArgs, executionQueueCmd.mWorkingDir, executionQueueCmd.mUseArgsFile, executionQueueCmd.mEnvVars, executionQueueCmd.mStdInData);
                     executionInstance.mParallelGroup = executionQueueCmd.mParallelGroup;
 					executionInstance.mIsTargetRun = executionQueueCmd.mIsTargetRun;
                 }
@@ -7903,12 +7940,23 @@ namespace IDE
 						success = false;
 					}
 				}
+				else if (options.mBuildOptions.mBuildKind == .StaticLib)
+				{
+					if (project.mGeneralOptions.mTargetType.IsBeefApplication)
+						targetType = .BeefApplication_StaticLib;
+				}
+				else if (options.mBuildOptions.mBuildKind == .DynamicLib)
+				{
+					if (project.mGeneralOptions.mTargetType.IsBeefApplication)
+						targetType = .BeefApplication_DynamicLib;
+				}
 			}
 
             bfProject.SetOptions(targetType,
                 project.mBeefGlobalOptions.mStartupObject,
                 preprocessorMacros.mDefines,
-                optimizationLevel, ltoType, options.mBeefOptions.mMergeFunctions, options.mBeefOptions.mCombineLoads,
+                optimizationLevel, ltoType, options.mBeefOptions.mRelocType, options.mBeefOptions.mPICLevel,
+				options.mBeefOptions.mMergeFunctions, options.mBeefOptions.mCombineLoads,
                 options.mBeefOptions.mVectorizeLoops, options.mBeefOptions.mVectorizeSLP);
 
             List<Project> depProjectList = scope List<Project>();
@@ -8378,21 +8426,43 @@ namespace IDE
 										return false;
 
 									let platformType = Workspace.PlatformType.GetFromName(platformName);
-									switch (platformType)
+
+									if (options.mBuildOptions.mBuildKind.IsApplicationLib)
 									{
-									case .Windows:
-										if (project.mGeneralOptions.mTargetType == .BeefLib)
-										    newString.Append(".lib");
-										else if (project.mGeneralOptions.mTargetType == .BeefDynLib)
-										    newString.Append(".dll");
-										else if (project.mGeneralOptions.mTargetType != .CustomBuild)
-										    newString.Append(".exe");
-									case .macOS:
-										if (project.mGeneralOptions.mTargetType == Project.TargetType.BeefLib)
-											newString.Append(".dylib");
-									default:
-										if (project.mGeneralOptions.mTargetType == Project.TargetType.BeefLib)
-											newString.Append(".so");
+										switch (platformType)
+										{
+										case .Windows:
+											newString.Append(".lib");
+										case .iOS:
+											if (options.mBuildOptions.mBuildKind == .DynamicLib)
+												newString.Append(".dylib");
+											else
+												newString.Append(".a");
+										default:
+											if (options.mBuildOptions.mBuildKind == .DynamicLib)
+												newString.Append(".so");
+											else
+												newString.Append(".a");
+										}
+									}
+									else
+									{
+										switch (platformType)
+										{
+										case .Windows:
+											if (project.mGeneralOptions.mTargetType == .BeefLib)
+											    newString.Append(".lib");
+											else if (project.mGeneralOptions.mTargetType == .BeefDynLib)
+											    newString.Append(".dll");
+											else if (project.mGeneralOptions.mTargetType != .CustomBuild)
+											    newString.Append(".exe");
+										case .macOS:
+											if (project.mGeneralOptions.mTargetType == Project.TargetType.BeefLib)
+												newString.Append(".dylib");
+										default:
+											if (project.mGeneralOptions.mTargetType == Project.TargetType.BeefLib)
+												newString.Append(".so");
+										}
 									}
 		                        }
 		                    case "ProjectDir":
@@ -8633,7 +8703,10 @@ namespace IDE
             else
             {
 				clangOptions.Append("--target=");
-                Workspace.PlatformType.GetTargetTripleByName(gApp.mPlatformName, workspaceOptions.mToolsetType, clangOptions);
+				if (TargetTriple.IsTargetTriple(gApp.mPlatformName))
+					clangOptions.Append(gApp.mPlatformName);
+				else
+                	Workspace.PlatformType.GetTargetTripleByName(gApp.mPlatformName, workspaceOptions.mToolsetType, clangOptions);
 				clangOptions.Append(" ");
 
 				if (workspaceOptions.mToolsetType == .GNU)

+ 65 - 12
IDE/src/Project.bf

@@ -792,6 +792,16 @@ namespace IDE
 		{
 			case Normal;
 			case Test;
+			case StaticLib;
+			case DynamicLib;
+
+			public bool IsApplicationLib
+			{
+				get
+				{
+					return (this == .StaticLib) || (this == .DynamicLib);
+				}
+			}
 		}
 
         public enum COptimizationLevel
@@ -838,20 +848,39 @@ namespace IDE
 				 CustomBuild,
 				 C_ConsoleApplication,
 				 C_WindowsApplication,
-				 BeefTest;
+				 BeefTest,
+				 BeefApplication_StaticLib,
+				 BeefApplication_DynamicLib;
 
 		 	public bool IsBeef
 		 	{
-				 get
-				 {
-					 switch (this)
-					 {
-						 case BeefConsoleApplication,
-							  BeefWindowsApplication,
-							  BeefLib,
-							  BeefDynLib,
-							  BeefTest: return true;
-						 default: return false;
+				get
+				{
+					switch (this)
+					{
+					case BeefConsoleApplication,
+						 BeefWindowsApplication,
+						 BeefLib,
+						 BeefDynLib,
+						 BeefTest:
+						return true;
+					default:
+						return false;
+					}
+				}
+			}
+
+			public bool IsBeefApplication
+			{
+				get
+				{
+					switch (this)
+					{
+					case BeefConsoleApplication,
+						 BeefWindowsApplication:
+						return true;
+					default:
+						return false;
 					}
 				}
 			}
@@ -1005,6 +1034,10 @@ namespace IDE
 			[Reflect]
             public List<String> mPreprocessorMacros = new List<String>() ~ DeleteContainerAndItems!(_);
 			[Reflect]
+			public BuildOptions.RelocType mRelocType;
+			[Reflect]
+			public BuildOptions.PICLevel mPICLevel;
+			[Reflect]
             public BuildOptions.BfOptimizationLevel? mOptimizationLevel;
 			[Reflect]
 			public BuildOptions.LTOType? mLTOType;
@@ -1103,6 +1136,8 @@ namespace IDE
 				Set!(newOptions.mBeefOptions.mPreprocessorMacros, mBeefOptions.mPreprocessorMacros);
 				Set!(newOptions.mBeefOptions.mOptimizationLevel, mBeefOptions.mOptimizationLevel);
 				Set!(newOptions.mBeefOptions.mLTOType, mBeefOptions.mLTOType);
+				Set!(newOptions.mBeefOptions.mRelocType, mBeefOptions.mRelocType);
+				Set!(newOptions.mBeefOptions.mPICLevel, mBeefOptions.mPICLevel);
 				Set!(newOptions.mBeefOptions.mMergeFunctions, mBeefOptions.mMergeFunctions);
 				Set!(newOptions.mBeefOptions.mCombineLoads, mBeefOptions.mCombineLoads);
 				Set!(newOptions.mBeefOptions.mVectorizeLoops, mBeefOptions.mVectorizeLoops);
@@ -1522,6 +1557,8 @@ namespace IDE
 							                data.Add(macro);
 							        }
 								}
+								data.ConditionalAdd("RelocType", options.mBeefOptions.mRelocType, .NotSet);
+								data.ConditionalAdd("PICLevel", options.mBeefOptions.mPICLevel, .NotSet);
 							    data.ConditionalAdd("OptimizationLevel", options.mBeefOptions.mOptimizationLevel);
 								data.ConditionalAdd("LTOType", options.mBeefOptions.mLTOType);
 							    data.ConditionalAdd("MergeFunctions", options.mBeefOptions.mMergeFunctions);
@@ -1826,6 +1863,8 @@ namespace IDE
 							options.mBeefOptions.mPreprocessorMacros.Add(new String("TEST"));
 					}
 
+					options.mBeefOptions.mRelocType = data.GetEnum<BuildOptions.RelocType>("RelocType");
+					options.mBeefOptions.mPICLevel = data.GetEnum<BuildOptions.PICLevel>("PICLevel");
 					if (data.Contains("OptimizationLevel"))
 			        	options.mBeefOptions.mOptimizationLevel = data.GetEnum<BuildOptions.BfOptimizationLevel>("OptimizationLevel");
 					if (data.Contains("LTOType"))
@@ -2135,6 +2174,7 @@ namespace IDE
 			bool isParanoid = configName.Contains("Paranoid");
 			bool isDebug = isParanoid || configName.Contains("Debug");
 			bool isTest = configName.Contains("Test");
+			let platformType = Workspace.PlatformType.GetFromName(platformName);
 
 			if (isRelease)
 				options.mBeefOptions.mPreprocessorMacros.Add(new String("RELEASE"));
@@ -2149,7 +2189,20 @@ namespace IDE
 			options.mBuildOptions.mBeefLibType = isRelease ? .Static : .Dynamic;
 			options.mBuildOptions.mStackSize = 0;
 
-			options.mBuildOptions.mBuildKind = isTest ? .Test : .Normal;
+			switch (platformType)
+			{
+			case .Linux,
+				 .Windows,
+				 .macOS:
+				options.mBuildOptions.mBuildKind = isTest ? .Test : .Normal;
+			default:
+				options.mBuildOptions.mBuildKind = .StaticLib;
+			}
+
+			if (platformType == .Android)
+			{
+				options.mBeefOptions.mRelocType = .PIC;
+			}
 
 			options.mBuildOptions.mOtherLinkFlags.Set("$(LinkFlags)");
 

+ 20 - 5
IDE/src/Workspace.bf

@@ -27,6 +27,7 @@ namespace IDE
 			case Linux;
 			case macOS;
 			case iOS;
+			case Android;
 
 			public static PlatformType GetFromName(String name)
 			{
@@ -36,7 +37,8 @@ namespace IDE
 				case "Linux32", "Linux64": return .Linux;
 				case "macOS": return .macOS;
 				case "iOS": return .iOS;
-				default: return .Unknown;
+				default:
+					return TargetTriple.GetPlatformType(name);
 				}
 			}
 
@@ -60,7 +62,11 @@ namespace IDE
 
 			public static int GetPtrSizeByName(String name)
 			{
-				if (name.EndsWith("32"))
+				if ((name.EndsWith("32")) && (!TargetTriple.IsTargetTriple(name)))
+					return 4;
+				if (name.StartsWith("armv"))
+					return 4;
+				if (name.StartsWith("i686-"))
 					return 4;
 				return 8;
 			}
@@ -80,7 +86,7 @@ namespace IDE
 				case "macOS":
 					outTriple.Append("x86_64-apple-macosx10.14.0");
 				case "iOS":
-					outTriple.Append("aarch64-apple-ios");
+					outTriple.Append("arm64-apple-ios");
 				default:
 					return false;
 				}
@@ -167,6 +173,8 @@ namespace IDE
 		{
 			[Reflect]
 			public List<String> mPreprocessorMacros = new List<String>() ~ DeleteContainerAndItems!(_);
+			/*[Reflect]
+			public String mTargetTriple = new .() ~ delete _;*/
 			[Reflect]
 			public List<DistinctBuildOptions> mDistinctBuildOptions = new List<DistinctBuildOptions>() ~ DeleteContainerAndItems!(_);
 		}
@@ -505,6 +513,7 @@ namespace IDE
 				if (mStartupProject != null)
 					data.Add("StartupProject", mStartupProject.mProjectName);
 				WriteStrings("PreprocessorMacros", mBeefGlobalOptions.mPreprocessorMacros);
+				//data.ConditionalAdd("TargetTriple", mBeefGlobalOptions.mTargetTriple, "");
 				WriteDistinctOptions(mBeefGlobalOptions.mDistinctBuildOptions);
 				data.RemoveIfEmpty();
 			}
@@ -697,15 +706,20 @@ namespace IDE
 			bool isTest = configName.Contains("Test");
 			let platformType = PlatformType.GetFromName(platformName);
 
+			/*if (TargetTriple.IsTargetTriple(platformName))
+			{
+				options.mToolsetType = .None;
+			}*/
+
+			options.mBfOptimizationLevel = isRelease ? .O2 : .O0;
 			options.mBfSIMDSetting = .SSE2;
 			if (platformType == .Windows)
 			{
 				options.mBfOptimizationLevel = isRelease ? .O2 : (platformName == "Win64") ? .OgPlus : .O0;
 				options.mToolsetType = .Microsoft;
 			}
-			else
+			else if ((platformType == .macOS) == (platformType == .Linux))
 			{
-				options.mBfOptimizationLevel = isRelease ? .O2 : .O0;
 				options.mToolsetType = .GNU;
 			}
 
@@ -757,6 +771,7 @@ namespace IDE
 					data.GetCurString(str);
 				    mBeefGlobalOptions.mPreprocessorMacros.Add(str);
 				}
+				//data.GetString("TargetTriple", mBeefGlobalOptions.mTargetTriple);
 
 				for (data.Enumerate("DistinctOptions"))
 				{

+ 3 - 1
IDE/src/ui/ProjectProperties.bf

@@ -823,7 +823,9 @@ namespace IDE.ui
 
             (category, propEntry) = AddPropertiesItem(root, "Code Generation");
             category.mIsBold = true;
-            category.mTextColor = cHeaderColor;            
+            category.mTextColor = cHeaderColor;
+			AddPropertiesItem(category, "Reloc Model", "mBeefOptions.mRelocType");
+			AddPropertiesItem(category, "PIC Level", "mBeefOptions.mPICLevel");
             AddPropertiesItem(category, "Optimization Level", "mBeefOptions.mOptimizationLevel",
                 scope String[] { "O0", "O1", "O2", "O3", "Og", "Og+" }); // -O0 .. -O3,  -Os, -Ofast, -Og
 			AddPropertiesItem(category, "LTO", "mBeefOptions.mLTOType");

+ 7 - 4
IDE/src/ui/WorkspaceProperties.bf

@@ -724,17 +724,20 @@ namespace IDE.ui
 		void PopulateBeefGlobalOptions()
 		{
 		    var root = (DarkListViewItem)mPropPage.mPropertiesListView.GetRoot();
-		    var (category, ?) = AddPropertiesItem(root, "General");
+		    /*var (category, ?) = AddPropertiesItem(root, "General");
 		    category.mIsBold = true;
-		    category.mTextColor = 0xFFE8E8E8;
+		    category.mTextColor = 0xFFE8E8E8;*/
 			
-			AddPropertiesItem(category, "Preprocessor Macros", "mPreprocessorMacros");
+			AddPropertiesItem(root, "Preprocessor Macros", "mPreprocessorMacros");
 			DistinctOptionBuilder dictinctOptionBuilder = scope .(this);
 			dictinctOptionBuilder.Add(gApp.mWorkspace.mBeefGlobalOptions.mDistinctBuildOptions);
 			dictinctOptionBuilder.Finish();
+
+			//AddPropertiesItem(root, "Target Triple", "mTargetTriple");
+
 			AddNewDistinctBuildOptions();
 		    //parent.MakeParent();
-		    category.Open(true, true);
+		    //category.Open(true, true);
 		}
 
         void PopulateBeefTargetedOptions()

+ 55 - 0
IDE/src/util/TargetTriple.bf

@@ -0,0 +1,55 @@
+using System;
+
+namespace IDE.Util
+{
+	class TargetTriple
+	{
+		public static bool IsTargetTriple(StringView str)
+		{
+			int dashCount = 0;
+			for (let c in str.RawChars)
+				if (c == '-')
+					dashCount++;
+			return dashCount >= 2;
+		}
+
+		public static Workspace.PlatformType GetPlatformType(StringView str)
+		{
+			var str;
+
+			// Remove version from the end
+			while (!str.IsEmpty)
+			{
+				char8 c = str[str.Length - 1];
+				if ((c.IsDigit) || (c == '.'))
+					str.RemoveFromEnd(1);
+				else
+					break;
+			}
+
+			bool hasLinux = false;
+
+			for (let elem in str.Split('-'))
+			{
+				switch (elem)
+				{
+				case "linux":
+					hasLinux = true;
+				case "windows":
+					return .Windows;
+				case "macosx":
+					return .macOS;
+				case "ios":
+					return .iOS;
+				case "android",
+					 "androideabi":
+					return .Android;
+				}
+			}
+
+			if (hasLinux)
+				return .Linux;
+			return .Unknown;
+		}
+	}
+}

+ 62 - 15
IDEHelper/Compiler/BfCompiler.cpp

@@ -1582,7 +1582,7 @@ void BfCompiler::CreateVData(BfVDataModule* bfModule)
 		BfIRFunctionType mainFuncType;
 		BfIRFunction mainFunc;
 		if ((project->mTargetType == BfTargetType_BeefConsoleApplication) || (project->mTargetType == BfTargetType_BeefTest))
-		{			
+		{
 			SmallVector<BfIRType, 2> paramTypes;
 			paramTypes.push_back(int32Type);
 			paramTypes.push_back(nullPtrType);
@@ -1598,7 +1598,7 @@ void BfCompiler::CreateVData(BfVDataModule* bfModule)
 			paramTypes.push_back(nullPtrType); // lpvReserved			
 			mainFuncType = bfModule->mBfIRBuilder->CreateFunctionType(int32Type, paramTypes, false);
 			mainFunc = bfModule->mBfIRBuilder->CreateFunction(mainFuncType, BfIRLinkageType_External, "DllMain");
-			if (mSystem->mPtrSize == 4)
+			if (mOptions.mMachineType == BfMachineType_x86)
 				bfModule->mBfIRBuilder->SetFuncCallingConv(mainFunc, BfIRCallingConv_StdCall);
 			bfModule->SetupIRMethod(NULL, mainFunc, false);
 		}
@@ -1611,7 +1611,7 @@ void BfCompiler::CreateVData(BfVDataModule* bfModule)
 			paramTypes.push_back(int32Type); // nCmdShow
 			mainFuncType = bfModule->mBfIRBuilder->CreateFunctionType(int32Type, paramTypes, false);
 			mainFunc = bfModule->mBfIRBuilder->CreateFunction(mainFuncType, BfIRLinkageType_External, "WinMain");
-			if (mSystem->mPtrSize == 4)
+			if (mOptions.mMachineType == BfMachineType_x86)
 				bfModule->mBfIRBuilder->SetFuncCallingConv(mainFunc, BfIRCallingConv_StdCall);			
 			bfModule->SetupIRMethod(NULL, mainFunc, false);
 		}		
@@ -1620,7 +1620,7 @@ void BfCompiler::CreateVData(BfVDataModule* bfModule)
 			SmallVector<BfIRType, 2> paramTypes;
 			paramTypes.push_back(int32Type);
 			paramTypes.push_back(nullPtrType);
-			mainFuncType = bfModule->mBfIRBuilder->CreateFunctionType(voidType, paramTypes, false);
+			mainFuncType = bfModule->mBfIRBuilder->CreateFunctionType(int32Type, paramTypes, false);
 			mainFunc = bfModule->mBfIRBuilder->CreateFunction(mainFuncType, BfIRLinkageType_External, "BeefMain");
 			bfModule->SetupIRMethod(NULL, mainFunc, false);
 		}
@@ -1711,7 +1711,8 @@ void BfCompiler::CreateVData(BfVDataModule* bfModule)
 		}
 
 		BfIRValue retValue;
-		if ((project->mTargetType == BfTargetType_BeefConsoleApplication) || (project->mTargetType == BfTargetType_BeefWindowsApplication))
+		if ((project->mTargetType == BfTargetType_BeefConsoleApplication) || (project->mTargetType == BfTargetType_BeefWindowsApplication) ||
+			(project->mTargetType == BfTargetType_BeefApplication_StaticLib) || (project->mTargetType == BfTargetType_BeefApplication_DynamicLib))
 		{
 			bool hadRet = false;
 
@@ -1785,7 +1786,7 @@ void BfCompiler::CreateVData(BfVDataModule* bfModule)
 							}
 							BfIRFunctionType thunkFuncType = bfModule->mBfIRBuilder->CreateFunctionType(int32Type, paramTypes, false);
 							
-							BfIRFunction thunkMainFunc = bfModule->mBfIRBuilder->CreateFunction(thunkFuncType, BfIRLinkageType_External, "BeefMain");
+							BfIRFunction thunkMainFunc = bfModule->mBfIRBuilder->CreateFunction(thunkFuncType, BfIRLinkageType_External, "BeefStartProgram");
 							bfModule->SetupIRMethod(NULL, thunkMainFunc, false);
 							bfModule->mBfIRBuilder->SetActiveFunction(thunkMainFunc);
 
@@ -5879,7 +5880,8 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory)
 			if ((bfProject->mTargetType != BfTargetType_BeefConsoleApplication) && (bfProject->mTargetType != BfTargetType_BeefWindowsApplication) &&
 				(bfProject->mTargetType != BfTargetType_BeefDynLib) &&
 				(bfProject->mTargetType != BfTargetType_C_ConsoleApplication) && (bfProject->mTargetType != BfTargetType_C_WindowsApplication) &&
-				(bfProject->mTargetType != BfTargetType_BeefTest))
+				(bfProject->mTargetType != BfTargetType_BeefTest) &&
+				(bfProject->mTargetType != BfTargetType_BeefApplication_StaticLib) && (bfProject->mTargetType != BfTargetType_BeefApplication_DynamicLib))
 				continue;
 
 			if (bfProject->mTargetType == BfTargetType_BeefTest)
@@ -8122,6 +8124,38 @@ BF_EXPORT const char* BF_CALLTYPE BfCompiler_HotResolve_Finish(BfCompiler* bfCom
 	return outString.c_str();
 }
 
+static BfPlatformType GetPlatform(StringView str)
+{	
+	while (!str.IsEmpty())
+	{
+		char c = str[str.mLength - 1];
+		if (((c >= '0') && (c <= '9')) || (c == '.'))
+			str.RemoveFromEnd(1);
+		else
+			break;
+	}
+
+	bool hasLinux = false;
+
+	for (auto elem : str.Split('-'))
+	{
+		if (elem == "linux")
+			hasLinux = true;
+		else if (elem == "windows")
+			return BfPlatformType_Windows;
+		else if (elem == "macosx")
+			return BfPlatformType_macOS;
+		else if (elem == "ios")
+			return BfPlatformType_iOS;
+		else if ((elem == "android") || (elem == "androideabi"))
+			return BfPlatformType_Android;
+	}
+
+	if (hasLinux)
+		return BfPlatformType_Linux;
+	return BfPlatformType_Unknown;
+}
+
 BF_EXPORT void BF_CALLTYPE BfCompiler_SetOptions(BfCompiler* bfCompiler, BfProject* hotProject, int hotIdx,
 	const char* targetTriple, int toolsetType, int simdSetting, int allocStackCount, int maxWorkerThreads,
 	BfCompilerOptionFlags optionFlags, char* mallocLinkName, char* freeLinkName)
@@ -8141,9 +8175,22 @@ BF_EXPORT void BF_CALLTYPE BfCompiler_SetOptions(BfCompiler* bfCompiler, BfProje
 		options->mMachineType = BfMachineType_x64;
 	else if (options->mTargetTriple.StartsWith("i686-"))
 		options->mMachineType = BfMachineType_x86;
+	else if ((options->mTargetTriple.StartsWith("arm64")) || (options->mTargetTriple.StartsWith("aarch64")))
+		options->mMachineType = BfMachineType_AArch64;
+	else if (options->mTargetTriple.StartsWith("armv"))
+		options->mMachineType = BfMachineType_ARM;
 	else
 		options->mMachineType = BfMachineType_x64; // Default
 
+	options->mPlatformType = GetPlatform(options->mTargetTriple);
+
+	options->mCLongSize = 4;
+	if ((options->mMachineType == BfMachineType_AArch64) || (options->mMachineType == BfMachineType_x64))
+	{
+		if ((options->mPlatformType == BfPlatformType_macOS) || (options->mPlatformType == BfPlatformType_iOS) || (options->mPlatformType == BfPlatformType_Android))
+			options->mCLongSize = 8;
+	}	
+	
 	bfCompiler->mCodeGen.SetMaxThreads(maxWorkerThreads);
 
 	if (!bfCompiler->mIsResolveOnly)
@@ -8169,15 +8216,15 @@ BF_EXPORT void BF_CALLTYPE BfCompiler_SetOptions(BfCompiler* bfCompiler, BfProje
 		options->mOmitDebugHelpers = (optionFlags & BfCompilerOptionFlag_OmitDebugHelpers) != 0;
 
 #ifdef _WINDOWS
-		if (options->mToolsetType == BfToolsetType_GNU)
-		{
-			options->mErrorString = "Toolset 'GNU' is not available on this platform. Consider changing 'Workspace/General/Toolset'.";
-		}
+// 		if (options->mToolsetType == BfToolsetType_GNU)
+// 		{
+// 			options->mErrorString = "Toolset 'GNU' is not available on this platform. Consider changing 'Workspace/General/Toolset'.";
+// 		}
 #else
-		if (options->mToolsetType == BfToolsetType_Microsoft)
-		{
-			options->mErrorString = "Toolset 'Microsoft' is not available on this platform. Consider changing 'Workspace/General/Toolset'.";
-		}
+// 		if (options->mToolsetType == BfToolsetType_Microsoft)
+// 		{
+// 			options->mErrorString = "Toolset 'Microsoft' is not available on this platform. Consider changing 'Workspace/General/Toolset'.";
+// 		}
 		BF_ASSERT(!options->mEnableRealtimeLeakCheck);		
 #endif
 		options->mEmitObjectAccessCheck = (optionFlags & BfCompilerOptionFlag_EmitDebugInfo) != 0;

+ 5 - 1
IDEHelper/Compiler/BfCompiler.h

@@ -94,13 +94,15 @@ public:
 		int32 mForceRebuildIdx;
 		BfCompileOnDemandKind mCompileOnDemandKind;		
 		String mTargetTriple;
+		BfPlatformType mPlatformType;
 		BfMachineType mMachineType;
+		int mCLongSize;
 		BfToolsetType mToolsetType;
 		BfSIMDSetting mSIMDSetting;				
 		int mMaxWorkerThreads;
 		String mMallocLinkName;
 		String mFreeLinkName;
-		bool mIncrementalBuild;
+		bool mIncrementalBuild;		
 
 		bool mEmitDebugInfo;
 		bool mEmitLineInfo;
@@ -140,7 +142,9 @@ public:
 			mHotCompileIdx = 0;
 			mForceRebuildIdx = 0;
 			mCompileOnDemandKind = BfCompileOnDemandKind_AlwaysInclude;
+			mPlatformType = BfPlatformType_Unknown;
 			mMachineType = BfMachineType_x86;
+			mCLongSize = 4;
 			mToolsetType = BfToolsetType_Microsoft;
 			mSIMDSetting = BfSIMDSetting_None;
 			mHotProject = NULL;

+ 26 - 5
IDEHelper/Compiler/BfContext.cpp

@@ -1632,7 +1632,8 @@ void BfContext::UpdateRevisedTypes()
 	BP_ZONE("BfContext::UpdateRevisedTypes");
 
 	int wantPtrSize;
-	if (mCompiler->mOptions.mMachineType == BfMachineType_x86)
+	if ((mCompiler->mOptions.mMachineType == BfMachineType_x86) |
+		(mCompiler->mOptions.mMachineType == BfMachineType_ARM))
 		wantPtrSize = 4;
 	else
 		wantPtrSize = 8;
@@ -1788,9 +1789,12 @@ void BfContext::UpdateRevisedTypes()
 
 	//
 	{
+		AutoCrit autoCrit(mSystem->mDataLock);
+
 		auto options = &mCompiler->mOptions;
 		HashContext workspaceConfigHashCtx;												
 
+		workspaceConfigHashCtx.MixinStr(options->mTargetTriple);
 		workspaceConfigHashCtx.Mixin(options->mForceRebuildIdx);
 
 		workspaceConfigHashCtx.Mixin(options->mMachineType);
@@ -1813,6 +1817,8 @@ void BfContext::UpdateRevisedTypes()
 		workspaceConfigHashCtx.Mixin(options->mEnableCustodian);
 		workspaceConfigHashCtx.Mixin(options->mEnableSideStack);
 		workspaceConfigHashCtx.Mixin(options->mHasVDataExtender);
+		workspaceConfigHashCtx.Mixin(options->mDebugAlloc);
+		workspaceConfigHashCtx.Mixin(options->mOmitDebugHelpers);
 
 		workspaceConfigHashCtx.Mixin(options->mUseDebugBackingParams);
 
@@ -1821,6 +1827,7 @@ void BfContext::UpdateRevisedTypes()
 
 		workspaceConfigHashCtx.Mixin(options->mAllocStackCount);
 		workspaceConfigHashCtx.Mixin(options->mExtraResolveChecks);
+		workspaceConfigHashCtx.Mixin(options->mMaxSplatRegs);
 		workspaceConfigHashCtx.MixinStr(options->mMallocLinkName);
 		workspaceConfigHashCtx.MixinStr(options->mFreeLinkName);
 
@@ -1842,6 +1849,11 @@ void BfContext::UpdateRevisedTypes()
 			workspaceConfigHashCtx.Mixin(typeOptions.mAllocStackTraceDepth);
 		}
 
+// 		for (auto project : mSystem->mProjects)
+// 		{
+// 			workspaceConfigHashCtx.MixinStr(project->mName);
+// 		}
+
 		Val128 workspaceConfigHash = workspaceConfigHashCtx.Finish128();
 
 		mSystem->mWorkspaceConfigChanged = mSystem->mWorkspaceConfigHash != workspaceConfigHash;
@@ -1852,8 +1864,7 @@ void BfContext::UpdateRevisedTypes()
 			mSystem->mMergedTypeOptions.Clear();
 			mSystem->mWorkspaceConfigHash = workspaceConfigHash;
 		}
-
-		AutoCrit autoCrit(mSystem->mDataLock);
+		
 		for (auto project : mSystem->mProjects)
 		{
 			HashContext buildConfigHashCtx;
@@ -1861,11 +1872,14 @@ void BfContext::UpdateRevisedTypes()
 			
 			if (!mCompiler->mIsResolveOnly)
 			{
-				auto& codeGenOptions = project->mCodeGenOptions;
+				auto& codeGenOptions = project->mCodeGenOptions;								
 				
+				buildConfigHashCtx.Mixin(project->mAlwaysIncludeAll);
+				buildConfigHashCtx.Mixin(project->mSingleModule);
+
 				bool isTestConfig = project->mTargetType == BfTargetType_BeefTest;
 				buildConfigHashCtx.Mixin(isTestConfig);
-
+				
 				buildConfigHashCtx.Mixin(codeGenOptions.mOptLevel);
 				buildConfigHashCtx.Mixin(codeGenOptions.mSizeLevel);
 				buildConfigHashCtx.Mixin(codeGenOptions.mUseCFLAA);
@@ -1888,6 +1902,7 @@ void BfContext::UpdateRevisedTypes()
 				buildConfigHashCtx.Mixin(codeGenOptions.mRunSLPAfterLoopVectorization);
 				buildConfigHashCtx.Mixin(codeGenOptions.mUseGVNAfterVectorization);
 			}
+			buildConfigHashCtx.Mixin(project->mDisabled);
 			buildConfigHashCtx.Mixin(project->mTargetType);
 
 			for (auto dep : project->mDependencies)
@@ -2226,6 +2241,12 @@ String BfContext::GenerateModuleName(BfTypeInstance* typeInst)
 		name.RemoveToEnd(80);
 		name += "__";
 	}
+	for (int i = 0; i < (int)name.length(); i++)
+	{
+		char c = name[i];
+		if (c == '@')
+			name[i] = '_';
+	}
 
 	String tryName = name;
 	for (int i = 2; true; i++)

+ 2 - 2
IDEHelper/Compiler/BfIRBuilder.cpp

@@ -2110,7 +2110,7 @@ void BfIRBuilder::CreateTypeDeclaration(BfType* type, bool forceDefine)
 			String prefix = typeDef->mProject->mName + ".";
 			StringT<128> mangledName;
 			mangledName += prefix;
-			BfMangler::Mangle(mangledName, mModule->mCompiler->GetMangleKind(), typeInstance);
+			BfMangler::Mangle(mangledName, mModule->mCompiler->GetMangleKind(), typeInstance, typeInstance->mModule);
 			BfIRType irStructType = CreateStructType(mangledName);
 			if (type->IsObjectOrInterface())
 			{
@@ -4748,7 +4748,7 @@ BfIRValue BfIRBuilder::DbgLifetimeEnd(BfIRMDNode varInfo)
 }
 
 void BfIRBuilder::DbgCreateGlobalVariable(BfIRMDNode context, const StringImpl& name, const StringImpl& linkageName, BfIRMDNode file, int lineNumber, BfIRMDNode type, bool isLocalToUnit, BfIRValue val, BfIRMDNode decl)
-{		
+{	
 	WriteCmd(BfIRCmd_DbgCreateGlobalVariable, context, name, linkageName, file, lineNumber, type, isLocalToUnit, val, decl);	
 	NEW_CMD_INSERTED;
 }

+ 41 - 1
IDEHelper/Compiler/BfIRCodeGen.cpp

@@ -4009,9 +4009,44 @@ bool BfIRCodeGen::WriteObjectFile(const StringImpl& outFileName, const BfCodeGen
 	llvm::Optional<llvm::Reloc::Model> relocModel;
 	llvm::CodeModel::Model cmModel = llvm::CodeModel::Small;
 
+	switch (codeGenOptions.mRelocType)
+	{
+	case BfRelocType_Static:
+		relocModel = llvm::Reloc::Model::DynamicNoPIC;
+		break;
+	case BfRelocType_PIC:
+		relocModel = llvm::Reloc::Model::PIC_;
+		break;
+	case BfRelocType_DynamicNoPIC:
+		relocModel = llvm::Reloc::Model::DynamicNoPIC;
+		break;
+	case BfRelocType_ROPI:
+		relocModel = llvm::Reloc::Model::ROPI;
+		break;
+	case BfRelocType_RWPI:
+		relocModel = llvm::Reloc::Model::RWPI;
+		break;
+	case BfRelocType_ROPI_RWPI:
+		relocModel = llvm::Reloc::Model::ROPI_RWPI;
+		break;
+	}
+
+	switch (codeGenOptions.mPICLevel)
+	{
+	case BfPICLevel_Not:
+		mLLVMModule->setPICLevel(llvm::PICLevel::Level::NotPIC);
+		break;
+	case BfPICLevel_Small:
+		mLLVMModule->setPICLevel(llvm::PICLevel::Level::SmallPIC);
+		break;
+	case BfPICLevel_Big:
+		mLLVMModule->setPICLevel(llvm::PICLevel::Level::BigPIC);
+		break;
+	}
+
 	std::unique_ptr<llvm::TargetMachine> target(
 		theTarget->createTargetMachine(theTriple.getTriple(), cpuName.c_str(), featuresStr.c_str(),
-			Options, relocModel, cmModel, optLvl));
+			Options, relocModel, cmModel, optLvl));	
 
 	std::error_code EC;
 	llvm::sys::fs::OpenFlags OpenFlags = llvm::sys::fs::F_None;
@@ -4188,6 +4223,11 @@ void BfIRCodeGen::StaticInit()
 	LLVMInitializeX86AsmParser();
 	LLVMInitializeX86Disassembler();
 
+	LLVMInitializeARMTargetInfo();
+	LLVMInitializeARMTarget();
+	LLVMInitializeARMTargetMC();
+	LLVMInitializeARMAsmPrinter();
+
 	LLVMInitializeAArch64TargetInfo();
 	LLVMInitializeAArch64Target();
 	LLVMInitializeAArch64TargetMC();

+ 28 - 32
IDEHelper/Compiler/BfMangler.cpp

@@ -56,13 +56,10 @@ BfTypeCode BfGNUMangler::GetPrimTypeAt(MangleContext& mangleContext, StringImpl&
 	case 't': return BfTypeCode_UInt16;
 	case 'i': return BfTypeCode_Int32;
 	case 'j': return BfTypeCode_UInt32;
-#if __SIZEOF_LONG__ == 8
     case 'l': return BfTypeCode_Int64;
     case 'm': return BfTypeCode_UInt64;
-#else
     case 'x': return BfTypeCode_Int64;
     case 'y': return BfTypeCode_UInt64;
-#endif
 	case 'u': 
 		if (name[strIdx + 1] == '3')
 			return BfTypeCode_IntPtr;
@@ -234,9 +231,9 @@ void BfGNUMangler::FindOrCreateNameSub(MangleContext& mangleContext, StringImpl&
 		else
 			name += "4";		
 		if (genericParamType->mGenericParamKind == BfGenericParamKind_Method)
-			name += "@M";
+			name += "`M";
 		else
-			name += "@T";		
+			name += "`T";		
 		itoa(genericParamType->mGenericParamIdx, str, 10);
 		name += str;
 	}
@@ -274,7 +271,7 @@ void BfGNUMangler::FindOrCreateNameSub(MangleContext& mangleContext, StringImpl&
 }
 
 void BfGNUMangler::MangleTypeInst(MangleContext& mangleContext, StringImpl& name, BfTypeInstance* typeInst, BfTypeInstance* postfixTypeInstance, bool* isEndOpen)
-{	
+{		
 	static int sCallCount = 0;
 	sCallCount++;
 	
@@ -289,7 +286,7 @@ void BfGNUMangler::MangleTypeInst(MangleContext& mangleContext, StringImpl& name
 			BfFieldDef* fieldDef = fieldInstance->GetFieldDef();
 			String fieldName = fieldDef->mName;
 			if ((fieldName[0] < '0') || (fieldName[0] > '9'))
-				name += StrFormat("U%d@%s", fieldName.length() + 1, fieldName.c_str());
+				name += StrFormat("U%d`%s", fieldName.length() + 1, fieldName.c_str());
 			Mangle(mangleContext, name, fieldInstance->mResolvedType, postfixTypeInstance);
 		}
 		name += "E";
@@ -444,25 +441,25 @@ void BfGNUMangler::Mangle(MangleContext& mangleContext, StringImpl& name, BfType
 			name += "i"; return;
 		case BfTypeCode_UInt32:
 			name += "j"; return;
-#if __SIZEOF_LONG__ == 8
-        case BfTypeCode_Int64:
-            name += "l"; return;
-        case BfTypeCode_UInt64:
-            name += "m"; return;
-#else
         case BfTypeCode_Int64:
-            name += "x"; return;
+			if (mangleContext.mModule->mCompiler->mOptions.mCLongSize == 8)
+				name += "l";
+			else
+				name += "x";
+			return;
         case BfTypeCode_UInt64:
-            name += "y"; return;
-#endif
+			if (mangleContext.mModule->mCompiler->mOptions.mCLongSize == 8)
+				name += "m";
+			else
+				name += "y";
+			return;
 		case BfTypeCode_UIntPtr:
 			if ((mangleContext.mCCompat) || (mangleContext.mInArgs))
 			{
-#if __SIZEOF_LONG__ == 8
-                name += (primType->mSize == 8) ? "m" : "j";
-#else
-                name += (primType->mSize == 8) ? "y" : "j";
-#endif
+				if (mangleContext.mModule->mCompiler->mOptions.mCLongSize == 8)
+					name += (primType->mSize == 8) ? "m" : "j";
+				else
+					name += (primType->mSize == 8) ? "y" : "j";
 				return;
 			}
 			name += "u4uint";
@@ -470,11 +467,10 @@ void BfGNUMangler::Mangle(MangleContext& mangleContext, StringImpl& name, BfType
 		case BfTypeCode_IntPtr:
 			if ((mangleContext.mCCompat) || (mangleContext.mInArgs))
 			{
-#if __SIZEOF_LONG__ == 8
-                name += (primType->mSize == 8) ? "l" : "i";
-#else
-                name += (primType->mSize == 8) ? "x" : "i";
-#endif
+				if (mangleContext.mModule->mCompiler->mOptions.mCLongSize == 8)
+					name += (primType->mSize == 8) ? "l" : "i";
+				else
+					name += (primType->mSize == 8) ? "x" : "i";
 				return;
 			}
 			name += "u3int";
@@ -635,7 +631,7 @@ void BfGNUMangler::Mangle(MangleContext& mangleContext, StringImpl& name, BfType
 			}
 
 			name += strP;
-			name += '@';
+			name += '`';
 		}		
 	}
 	else
@@ -834,7 +830,7 @@ String BfGNUMangler::Mangle(BfMethodInstance* methodInst)
 	{
 		if (methodInst->mMangleWithIdx)
 		{
-			methodName += StrFormat("@%d", methodInst->mMethodDef->mIdx);
+			methodName += StrFormat("`%d", methodInst->mMethodDef->mIdx);
 			mangledMethodIdx = true;
 		}
 
@@ -842,9 +838,9 @@ String BfGNUMangler::Mangle(BfMethodInstance* methodInst)
 	}
 
 	if (methodDef->mCheckedKind == BfCheckedKind_Checked)
-		name += "@CHK";
+		name += "`CHK";
 	else if (methodDef->mCheckedKind == BfCheckedKind_Unchecked)
-		name += "@UCHK";
+		name += "`UCHK";
 
 	if ((methodInst->mMethodDef->mDeclaringType->mPartialIdx != -1) && (!methodInst->mIsForeignMethodDef))
 	{
@@ -877,14 +873,14 @@ String BfGNUMangler::Mangle(BfMethodInstance* methodInst)
 
 	if ((methodInst->mMangleWithIdx) && (!mangledMethodIdx))
 	{
-		methodName += StrFormat("@%d", methodInst->mMethodDef->mIdx);
+		methodName += StrFormat("`%d", methodInst->mMethodDef->mIdx);
 	}
 
 	//
 
 	if ((prefixLen) && (methodInst->mMethodInstanceGroup->mOwner->mTypeDef->IsGlobalsContainer()) && (methodInst->mMethodDef->mMethodDeclaration == NULL))
 	{
-		methodName += '@';
+		methodName += '`';
 		methodName += methodInst->mMethodInstanceGroup->mOwner->mTypeDef->mProject->mName;
 	}
 

+ 5 - 5
IDEHelper/Compiler/BfModule.cpp

@@ -4319,12 +4319,12 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary<int, int>& usedStrin
 	{
 		BfMangler::MangleStaticFieldName(typeDataName, mCompiler->GetMangleKind(), typeInstance, "sBfTypeData");
 		if (typeInstance->mTypeDef->IsGlobalsContainer())
-			typeDataName += "@" + typeInstance->mTypeDef->mProject->mName;
+			typeDataName += "`G`" + typeInstance->mTypeDef->mProject->mName;
 	}
 	else
 	{
 		typeDataName += "sBfTypeData.";
-		BfMangler::Mangle(typeDataName, mCompiler->GetMangleKind(), type);
+		BfMangler::Mangle(typeDataName, mCompiler->GetMangleKind(), type, mContext->mScratchModule);
 	}
 	
 	int typeCode = BfTypeCode_None;		
@@ -4460,7 +4460,7 @@ BfIRValue BfModule::CreateTypeData(BfType* type, Dictionary<int, int>& usedStrin
 	
 	BfTypeDef* typeDef = typeInstance->mTypeDef;
 	StringT<128> mangledName;
-	BfMangler::Mangle(mangledName, mCompiler->GetMangleKind(), typeInstance);
+	BfMangler::Mangle(mangledName, mCompiler->GetMangleKind(), typeInstance, typeInstance->mModule);
 	
 	for (int methodIdx = 0; methodIdx < (int)typeDef->mMethods.size(); methodIdx++)
 	{
@@ -13619,8 +13619,8 @@ void BfModule::CreateDllImportMethod()
 
 BfIRCallingConv BfModule::GetCallingConvention(BfTypeInstance* typeInst, BfMethodDef* methodDef)
 {
-	if (mSystem->mPtrSize == 8)
-		return BfIRCallingConv_CDecl;
+	if ((mCompiler->mOptions.mMachineType != BfMachineType_x86) || (mCompiler->mOptions.mPlatformType != BfPlatformType_Windows))
+		return BfIRCallingConv_CDecl;			
 	if (methodDef->mCallingConvention == BfCallingConvention_Stdcall)
 		return BfIRCallingConv_StdCall;
 	if ((!methodDef->mIsStatic) && (!typeInst->IsValuelessType()) &&

+ 4 - 2
IDEHelper/Compiler/BfSystem.cpp

@@ -3592,8 +3592,8 @@ BF_EXPORT void BF_CALLTYPE BfProject_SetDisabled(BfProject* bfProject, bool disa
 	bfProject->mDisabled = disabled;
 }
 
-BF_EXPORT void BF_CALLTYPE BfProject_SetOptions(BfProject* bfProject, int targetType, const char* startupObject, const char* preprocessorMacros, 
-	int optLevel, int ltoType, BfProjectFlags flags)
+BF_EXPORT void BF_CALLTYPE BfProject_SetOptions(BfProject* bfProject, int targetType, const char* startupObject, const char* preprocessorMacros,
+	int optLevel, int ltoType, int relocType, int picLevel, BfProjectFlags flags)
 {
 	bfProject->mTargetType = (BfTargetType)targetType;
 	bfProject->mStartupObject = startupObject;	
@@ -3601,6 +3601,8 @@ BF_EXPORT void BF_CALLTYPE BfProject_SetOptions(BfProject* bfProject, int target
 	BfCodeGenOptions codeGenOptions;
 	codeGenOptions.mOptLevel = (BfOptLevel)optLevel;
 	codeGenOptions.mLTOType = (BfLTOType)ltoType;
+	codeGenOptions.mRelocType = (BfRelocType)relocType;
+	codeGenOptions.mPICLevel = (BfPICLevel)picLevel;
 	codeGenOptions.mMergeFunctions = (flags & BfProjectFlags_MergeFunctions) != 0;
 	codeGenOptions.mLoadCombine = (flags & BfProjectFlags_CombineLoads) != 0;
 	codeGenOptions.mLoopVectorize = (flags & BfProjectFlags_VectorizeLoops) != 0;

+ 40 - 2
IDEHelper/Compiler/BfSystem.h

@@ -204,11 +204,22 @@ enum BfCustomAttributeFlags
 	BfCustomAttributeFlags_AlwaysIncludeTarget = 8
 };
 
+enum BfPlatformType
+{
+	BfPlatformType_Unknown,
+	BfPlatformType_Windows,
+	BfPlatformType_Linux,
+	BfPlatformType_macOS,
+	BfPlatformType_iOS,
+	BfPlatformType_Android,
+};
+
 enum BfMachineType
 {
 	BfMachineType_Unknown,
 	BfMachineType_x86,
 	BfMachineType_x64,
+	BfMachineType_ARM,
 	BfMachineType_AArch64
 };
 
@@ -265,6 +276,25 @@ enum BfCFLAAType
 	BfCFLAAType_Both
 };
 
+enum BfRelocType
+{
+	BfRelocType_NotSet,
+	BfRelocType_Static, 
+	BfRelocType_PIC, 
+	BfRelocType_DynamicNoPIC,
+	BfRelocType_ROPI,
+	BfRelocType_RWPI, 
+	BfRelocType_ROPI_RWPI
+};
+
+enum BfPICLevel
+{
+	BfPICLevel_NotSet,
+	BfPICLevel_Not,
+	BfPICLevel_Small, 
+	BfPICLevel_Big
+};
+
 struct BfCodeGenOptions
 {	
 	bool mIsHotCompile;
@@ -277,6 +307,8 @@ struct BfCodeGenOptions
 	int16 mVirtualMethodOfs;
 	int16 mDynSlotOfs;
 
+	BfRelocType mRelocType;
+	BfPICLevel mPICLevel;
 	BfSIMDSetting mSIMDSetting;	
 	BfOptLevel mOptLevel;
 	BfLTOType mLTOType;
@@ -327,7 +359,9 @@ struct BfCodeGenOptions
 		mWriteLLVMIR = false;
 		mVirtualMethodOfs = 0;
 		mDynSlotOfs = 0;
-
+		
+		mRelocType = BfRelocType_NotSet;
+		mPICLevel = BfPICLevel_NotSet;
 		mSIMDSetting = BfSIMDSetting_None;
 		mOptLevel = BfOptLevel_O0;
 		mLTOType = BfLTOType_None;
@@ -378,6 +412,8 @@ struct BfCodeGenOptions
 		hashCtx.Mixin(mVirtualMethodOfs);
 		hashCtx.Mixin(mDynSlotOfs);
 
+		hashCtx.Mixin(mRelocType);
+		hashCtx.Mixin(mPICLevel);
 		hashCtx.Mixin(mSIMDSetting);
 		hashCtx.Mixin(mOptLevel);
 		hashCtx.Mixin(mLTOType);
@@ -950,7 +986,9 @@ enum BfTargetType
 	BfTargetType_CustomBuild,
 	BfTargetType_C_ConsoleApplication,
 	BfTargetType_C_WindowsApplication,
-	BfTargetType_BeefTest
+	BfTargetType_BeefTest,
+	BfTargetType_BeefApplication_StaticLib,
+	BfTargetType_BeefApplication_DynamicLib
 };
 
 enum BfProjectFlags

+ 2 - 2
IDEHelper/IDEHelper.vcxproj

@@ -171,7 +171,7 @@
       <SubSystem>Windows</SubSystem>
       <GenerateDebugInformation>DebugFull</GenerateDebugInformation>
       <OutputFile>$(SolutionDir)\IDE\dist\$(TargetName).dll</OutputFile>
-      <AdditionalDependencies>rpcrt4.lib;cabinet.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;libcurl_a.lib;wininet.lib;LLVMMCDisassembler.lib;LLVMSupport.lib;LLVMMC.lib;LLVMObject.lib;LLVMCore.lib;LLVMBitReader.lib;LLVMAsmParser.lib;LLVMMCParser.lib;LLVMCodeGen.lib;LLVMTarget.lib;LLVMScalarOpts.lib;LLVMInstCombine.lib;LLVMSelectionDAG.lib;LLVMProfileData.lib;LLVMTransformUtils.lib;LLVMAnalysis.lib;LLVMAsmPrinter.lib;LLVMBitWriter.lib;LLVMVectorize.lib;LLVMipo.lib;LLVMInstrumentation.lib;LLVMDebugInfoDWARF.lib;LLVMDebugInfoPDB.lib;LLVMDebugInfoCodeView.lib;LLVMGlobalISel.lib;LLVMBinaryFormat.lib;LLVMLTO.lib;LLVMPasses.lib;LLVMLinker.lib;LLVMIRReader.lib;LLVMDemangle.lib;LLVMAggressiveInstCombine.lib;LLVMX86Info.lib;LLVMX86Utils.lib;LLVMX86AsmPrinter.lib;LLVMX86Desc.lib;LLVMX86CodeGen.lib;LLVMX86AsmParser.lib;LLVMX86Disassembler.lib;LLVMAArch64Info.lib;LLVMAArch64Utils.lib;LLVMAArch64AsmPrinter.lib;LLVMAArch64Desc.lib;LLVMAArch64CodeGen.lib;LLVMAArch64AsmParser.lib;LLVMAArch64Disassembler.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>rpcrt4.lib;cabinet.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;libcurl_a.lib;wininet.lib;LLVMMCDisassembler.lib;LLVMSupport.lib;LLVMMC.lib;LLVMObject.lib;LLVMCore.lib;LLVMBitReader.lib;LLVMAsmParser.lib;LLVMMCParser.lib;LLVMCodeGen.lib;LLVMTarget.lib;LLVMScalarOpts.lib;LLVMInstCombine.lib;LLVMSelectionDAG.lib;LLVMProfileData.lib;LLVMTransformUtils.lib;LLVMAnalysis.lib;LLVMAsmPrinter.lib;LLVMBitWriter.lib;LLVMVectorize.lib;LLVMipo.lib;LLVMInstrumentation.lib;LLVMDebugInfoDWARF.lib;LLVMDebugInfoPDB.lib;LLVMDebugInfoCodeView.lib;LLVMGlobalISel.lib;LLVMBinaryFormat.lib;LLVMLTO.lib;LLVMPasses.lib;LLVMLinker.lib;LLVMIRReader.lib;LLVMDemangle.lib;LLVMAggressiveInstCombine.lib;LLVMX86Info.lib;LLVMX86Utils.lib;LLVMX86AsmPrinter.lib;LLVMX86Desc.lib;LLVMX86CodeGen.lib;LLVMX86AsmParser.lib;LLVMX86Disassembler.lib;LLVMAArch64Info.lib;LLVMAArch64Utils.lib;LLVMAArch64AsmPrinter.lib;LLVMAArch64Desc.lib;LLVMAArch64CodeGen.lib;LLVMAArch64AsmParser.lib;LLVMAArch64Disassembler.lib;LLVMARMInfo.lib;LLVMARMUtils.lib;LLVMARMAsmPrinter.lib;LLVMARMDesc.lib;LLVMARMCodeGen.lib;LLVMARMAsmParser.lib;LLVMARMDisassembler.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <AdditionalLibraryDirectories>..\extern\llvm_win64_8_0_1\Debug\lib; ..\extern\curl\builds\libcurl-vc15-x64-release-static-zlib-static-ipv6-sspi-winssl\lib;..\extern\curl\deps\lib;..\extern\jemalloc_win\x64\debug</AdditionalLibraryDirectories>
       <RandomizedBaseAddress>false</RandomizedBaseAddress>
       <ImportLibrary>$(SolutionDir)\IDE\dist\$(TargetName).lib</ImportLibrary>
@@ -230,7 +230,7 @@
       <OptimizeReferences>true</OptimizeReferences>
       <OutputFile>$(SolutionDir)\IDE\dist\$(TargetName).dll</OutputFile>
       <AdditionalLibraryDirectories>..\extern\llvm_win64_8_0_1\Release\lib; ..\extern\curl\builds\libcurl-vc15-x64-release-static-zlib-static-ipv6-sspi-winssl\lib;..\extern\curl\deps\lib;..\extern\jemalloc_win\x64\release</AdditionalLibraryDirectories>
-      <AdditionalDependencies>rpcrt4.lib;cabinet.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;libcurl_a.lib;wininet.lib;LLVMMCDisassembler.lib;LLVMSupport.lib;LLVMMC.lib;LLVMObject.lib;LLVMCore.lib;LLVMBitReader.lib;LLVMAsmParser.lib;LLVMMCParser.lib;LLVMCodeGen.lib;LLVMTarget.lib;LLVMScalarOpts.lib;LLVMInstCombine.lib;LLVMSelectionDAG.lib;LLVMProfileData.lib;LLVMTransformUtils.lib;LLVMAnalysis.lib;LLVMAsmPrinter.lib;LLVMBitWriter.lib;LLVMVectorize.lib;LLVMipo.lib;LLVMInstrumentation.lib;LLVMDebugInfoDWARF.lib;LLVMDebugInfoPDB.lib;LLVMDebugInfoCodeView.lib;LLVMGlobalISel.lib;LLVMBinaryFormat.lib;LLVMLTO.lib;LLVMPasses.lib;LLVMLinker.lib;LLVMIRReader.lib;LLVMDemangle.lib;LLVMAggressiveInstCombine.lib;LLVMX86Info.lib;LLVMX86Utils.lib;LLVMX86AsmPrinter.lib;LLVMX86Desc.lib;LLVMX86CodeGen.lib;LLVMX86AsmParser.lib;LLVMX86Disassembler.lib;LLVMAArch64Info.lib;LLVMAArch64Utils.lib;LLVMAArch64AsmPrinter.lib;LLVMAArch64Desc.lib;LLVMAArch64CodeGen.lib;LLVMAArch64AsmParser.lib;LLVMAArch64Disassembler.lib;%(AdditionalDependencies)</AdditionalDependencies>
+      <AdditionalDependencies>rpcrt4.lib;cabinet.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;libcurl_a.lib;wininet.lib;LLVMMCDisassembler.lib;LLVMSupport.lib;LLVMMC.lib;LLVMObject.lib;LLVMCore.lib;LLVMBitReader.lib;LLVMAsmParser.lib;LLVMMCParser.lib;LLVMCodeGen.lib;LLVMTarget.lib;LLVMScalarOpts.lib;LLVMInstCombine.lib;LLVMSelectionDAG.lib;LLVMProfileData.lib;LLVMTransformUtils.lib;LLVMAnalysis.lib;LLVMAsmPrinter.lib;LLVMBitWriter.lib;LLVMVectorize.lib;LLVMipo.lib;LLVMInstrumentation.lib;LLVMDebugInfoDWARF.lib;LLVMDebugInfoPDB.lib;LLVMDebugInfoCodeView.lib;LLVMGlobalISel.lib;LLVMBinaryFormat.lib;LLVMLTO.lib;LLVMPasses.lib;LLVMLinker.lib;LLVMIRReader.lib;LLVMDemangle.lib;LLVMAggressiveInstCombine.lib;LLVMX86Info.lib;LLVMX86Utils.lib;LLVMX86AsmPrinter.lib;LLVMX86Desc.lib;LLVMX86CodeGen.lib;LLVMX86AsmParser.lib;LLVMX86Disassembler.lib;LLVMAArch64Info.lib;LLVMAArch64Utils.lib;LLVMAArch64AsmPrinter.lib;LLVMAArch64Desc.lib;LLVMAArch64CodeGen.lib;LLVMAArch64AsmParser.lib;LLVMAArch64Disassembler.lib;LLVMARMInfo.lib;LLVMARMUtils.lib;LLVMARMAsmPrinter.lib;LLVMARMDesc.lib;LLVMARMCodeGen.lib;LLVMARMAsmParser.lib;LLVMARMDisassembler.lib;%(AdditionalDependencies)</AdditionalDependencies>
       <RandomizedBaseAddress>false</RandomizedBaseAddress>
       <FullProgramDatabaseFile>true</FullProgramDatabaseFile>
       <ImportLibrary>$(SolutionDir)\IDE\dist\$(TargetName).lib</ImportLibrary>

+ 210 - 0
bin/build_android.bat

@@ -0,0 +1,210 @@
+PUSHD %~dp0..\
+
+SET NDK=C:\Users\Brian\AppData\Local\Android\Sdk\ndk\20.0.5594570
+SET NINJA=C:\Users\Brian\AppData\Local\Android\Sdk\cmake\3.10.2.4988404\bin\ninja.exe
+@REM SET NDK=C:\NVPACK\android-ndk-r14b
+
+@REM i686-none-linux-android16
+@REM i686-linux-android armv7-none-linux-androideabi16 
+
+cd builds
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR
+
+@IF EXIST android_x86 GOTO DO_BUILD
+
+mkdir android_x86_d
+cd android_x86_d
+cmake -GNinja ^
+	-DANDROID_ABI:UNINITIALIZED=x86 ^
+	-DANDROID_NDK=%NDK% ^
+	-DANDROID_PLATFORM=android-16 ^
+	-DCMAKE_ANDROID_ARCH_ABI=x86 ^
+	-DCMAKE_ANDROID_NDK=%NDK% ^
+	-DCMAKE_SYSTEM_NAME=Android ^
+	-DCMAKE_SYSTEM_VERSION=16 ^
+	-DCMAKE_TOOLCHAIN_FILE=%NDK%\build\cmake\android.toolchain.cmake ^
+	-DCMAKE_MAKE_PROGRAM=%NINJA% ^
+	-DCMAKE_BUILD_TYPE=Debug ^
+	..\..\BeefRT
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR	
+cd ..
+
+mkdir android_x86
+cd android_x86
+cmake -GNinja ^
+	-DANDROID_ABI:UNINITIALIZED=x86 ^
+	-DANDROID_NDK=%NDK% ^
+	-DANDROID_PLATFORM=android-16 ^
+	-DCMAKE_ANDROID_ARCH_ABI=x86 ^
+	-DCMAKE_ANDROID_NDK=%NDK% ^
+	-DCMAKE_SYSTEM_NAME=Android ^
+	-DCMAKE_SYSTEM_VERSION=16 ^
+	-DCMAKE_TOOLCHAIN_FILE=%NDK%\build\cmake\android.toolchain.cmake ^
+	-DCMAKE_MAKE_PROGRAM=%NINJA% ^
+	-DCMAKE_BUILD_TYPE=Release ^
+	..\..\BeefRT
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR	
+cd ..
+
+mkdir android_x86_64_d
+cd android_x86_64_d
+cmake -GNinja ^
+	-DANDROID_ABI:UNINITIALIZED=x86_64 ^
+	-DANDROID_NDK=%NDK% ^
+	-DANDROID_PLATFORM=android-16 ^
+	-DCMAKE_ANDROID_ARCH_ABI=x86_64 ^
+	-DCMAKE_ANDROID_NDK=%NDK% ^
+	-DCMAKE_SYSTEM_NAME=Android ^
+	-DCMAKE_SYSTEM_VERSION=16 ^
+	-DCMAKE_TOOLCHAIN_FILE=%NDK%\build\cmake\android.toolchain.cmake ^
+	-DCMAKE_MAKE_PROGRAM=%NINJA% ^
+	-DCMAKE_BUILD_TYPE=Debug ^
+	..\..\BeefRT
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR	
+cd ..
+
+mkdir android_x86_64
+cd android_x86_64
+cmake -GNinja ^
+	-DANDROID_ABI:UNINITIALIZED=x86_64 ^
+	-DANDROID_NDK=%NDK% ^
+	-DANDROID_PLATFORM=android-16 ^
+	-DCMAKE_ANDROID_ARCH_ABI=x86_64 ^
+	-DCMAKE_ANDROID_NDK=%NDK% ^
+	-DCMAKE_SYSTEM_NAME=Android ^
+	-DCMAKE_SYSTEM_VERSION=16 ^
+	-DCMAKE_TOOLCHAIN_FILE=%NDK%\build\cmake\android.toolchain.cmake ^
+	-DCMAKE_MAKE_PROGRAM=%NINJA% ^
+	-DCMAKE_BUILD_TYPE=Release ^
+	..\..\BeefRT
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR	
+cd ..
+
+mkdir android_arm_d
+cd android_arm_d
+cmake -GNinja ^
+	-DANDROID_ABI:UNINITIALIZED=armeabi-v7a ^
+	-DANDROID_NDK=%NDK% ^
+	-DANDROID_PLATFORM=android-16 ^
+	-DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a ^
+	-DCMAKE_ANDROID_NDK=%NDK% ^
+	-DCMAKE_SYSTEM_NAME=Android ^
+	-DCMAKE_SYSTEM_VERSION=16 ^
+	-DCMAKE_TOOLCHAIN_FILE=%NDK%\build\cmake\android.toolchain.cmake ^
+	-DCMAKE_MAKE_PROGRAM=%NINJA% ^
+	-DCMAKE_BUILD_TYPE=Debug ^
+	..\..\BeefRT
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR	
+cd ..
+
+mkdir android_arm
+cd android_arm
+cmake -GNinja ^
+	-DANDROID_ABI:UNINITIALIZED=armeabi-v7a ^
+	-DANDROID_NDK=%NDK% ^
+	-DANDROID_PLATFORM=android-16 ^
+	-DCMAKE_ANDROID_ARCH_ABI=armeabi-v7a ^
+	-DCMAKE_ANDROID_NDK=%NDK% ^
+	-DCMAKE_SYSTEM_NAME=Android ^
+	-DCMAKE_SYSTEM_VERSION=16 ^
+	-DCMAKE_TOOLCHAIN_FILE=%NDK%\build\cmake\android.toolchain.cmake ^
+	-DCMAKE_MAKE_PROGRAM=%NINJA% ^
+	-DCMAKE_BUILD_TYPE=Release ^
+	..\..\BeefRT
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR	
+cd ..
+
+mkdir android_arm64_d
+cd android_arm64_d
+cmake -GNinja ^
+	-DANDROID_ABI:UNINITIALIZED=arm64-v8a ^
+	-DANDROID_NDK=%NDK% ^
+	-DANDROID_PLATFORM=android-21 ^
+	-DCMAKE_ANDROID_ARCH_ABI=arm64-v8a ^
+	-DCMAKE_ANDROID_NDK=%NDK% ^
+	-DCMAKE_SYSTEM_NAME=Android ^
+	-DCMAKE_SYSTEM_VERSION=21 ^
+	-DCMAKE_TOOLCHAIN_FILE=%NDK%\build\cmake\android.toolchain.cmake ^
+	-DCMAKE_MAKE_PROGRAM=%NINJA% ^
+	-DCMAKE_BUILD_TYPE=Debug ^
+	..\..\BeefRT
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR	
+cd ..
+
+mkdir android_arm64
+cd android_arm64
+cmake -GNinja ^
+	-DANDROID_ABI:UNINITIALIZED=arm64-v8a ^
+	-DANDROID_NDK=%NDK% ^
+	-DANDROID_PLATFORM=android-21 ^
+	-DCMAKE_ANDROID_ARCH_ABI=arm64-v8a ^
+	-DCMAKE_ANDROID_NDK=%NDK% ^
+	-DCMAKE_SYSTEM_NAME=Android ^
+	-DCMAKE_SYSTEM_VERSION=21 ^
+	-DCMAKE_TOOLCHAIN_FILE=%NDK%\build\cmake\android.toolchain.cmake ^
+	-DCMAKE_MAKE_PROGRAM=%NINJA% ^
+	-DCMAKE_BUILD_TYPE=Release ^
+	..\..\BeefRT
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR	
+cd ..
+
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR
+:DO_BUILD
+
+cd android_x86_d
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR
+cmake --build .
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR
+cd ..
+
+cd android_x86
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR
+cmake --build .
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR
+cd ..
+
+cd android_x86_64_d
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR
+cmake --build .
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR
+cd ..
+
+cd android_x86_64
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR
+cmake --build .
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR
+cd ..
+
+cd android_arm_d
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR
+cmake --build .
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR
+cd ..
+
+cd android_arm
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR
+cmake --build .
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR
+cd ..
+
+cd android_arm64_d
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR
+cmake --build .
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR
+cd ..
+
+cd android_arm64
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR
+cmake --build .
+@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR
+cd ..
+
+:SUCCESS
+@ECHO SUCCESS!
+@POPD
+@EXIT /b 0
+
+:HADERROR
+@ECHO =================FAILED=================
+@POPD
+@EXIT /b %ERRORLEVEL%

部分文件因为文件数量过多而无法显示