diff --git a/misc/buildbot.sh b/misc/buildbot.sh index 69f4686fc..0d15db302 100644 --- a/misc/buildbot.sh +++ b/misc/buildbot.sh @@ -62,26 +62,26 @@ build_nix32() { echo "Building linux32.." cp $SOURCE_DIR/misc/CCicon_nix32 $SOURCE_DIR/src/CCicon_nix32.o rm cc-nix32 - gcc *.c $ALL_FLAGS $LINUX_FLAGS CCicon_nix32.o -DCC_COMMIT_SHA=\"$LATEST\" -m32 -o cc-nix32 -lX11 -lXi -lpthread -lGL -lm -lcurl -ldl + gcc *.c $ALL_FLAGS $LINUX_FLAGS CCicon_nix32.o -DCC_COMMIT_SHA=\"$LATEST\" -m32 -o cc-nix32 -lX11 -lXi -lpthread -lGL -lm -ldl } build_nix64() { echo "Building linux64.." cp $SOURCE_DIR/misc/CCicon_nix64 $SOURCE_DIR/src/CCicon_nix64.o rm cc-nix64 - gcc *.c $ALL_FLAGS $LINUX_FLAGS CCicon_nix64.o -DCC_COMMIT_SHA=\"$LATEST\" -m64 -o cc-nix64 -lX11 -lXi -lpthread -lGL -lm -lcurl -ldl + gcc *.c $ALL_FLAGS $LINUX_FLAGS CCicon_nix64.o -DCC_COMMIT_SHA=\"$LATEST\" -m64 -o cc-nix64 -lX11 -lXi -lpthread -lGL -lm -ldl } build_osx32() { echo "Building mac32.." rm cc-osx32 - $MAC32_CC *.c $ALL_FLAGS -fvisibility=hidden -rdynamic -DCC_COMMIT_SHA=\"$LATEST\" -o cc-osx32 -framework Carbon -framework AGL -framework OpenAL -framework OpenGL -lcurl + $MAC32_CC *.c $ALL_FLAGS -fvisibility=hidden -rdynamic -DCC_COMMIT_SHA=\"$LATEST\" -o cc-osx32 -framework Carbon -framework AGL -framework OpenGL } build_osx64() { echo "Building mac64.." rm cc-osx64 - $MAC64_CC *.c $ALL_FLAGS -fvisibility=hidden -rdynamic -DCC_COMMIT_SHA=\"$LATEST\" -o cc-osx64 -framework Cocoa -framework OpenAL -framework OpenGL -lcurl -lobjc + $MAC64_CC *.c $ALL_FLAGS -fvisibility=hidden -rdynamic -DCC_COMMIT_SHA=\"$LATEST\" -o cc-osx64 -framework Cocoa -framework OpenGL -lobjc } build_web() { @@ -101,7 +101,7 @@ build_rpi() { echo "Building rpi.." cp $SOURCE_DIR/misc/CCicon_rpi $SOURCE_DIR/src/CCicon_rpi.o rm cc-rpi - $RPI_CC *.c $ALL_FLAGS $LINUX_FLAGS CCicon_rpi.o -DCC_COMMIT_SHA=\"$LATEST\" -o cc-rpi -DCC_BUILD_RPI -I ~/rpi/include -L ~/rpi/lib -lGLESv2 -lEGL -lX11 -lXi -lcurl -lm -lpthread -ldl -lrt -Wl,-rpath-link ~/rpi/lib + $RPI_CC *.c $ALL_FLAGS $LINUX_FLAGS CCicon_rpi.o -DCC_COMMIT_SHA=\"$LATEST\" -o cc-rpi -DCC_BUILD_RPI -I ~/rpi/include -L ~/rpi/lib -lGLESv2 -lEGL -lX11 -lXi -lm -lpthread -ldl -lrt -Wl,-rpath-link ~/rpi/lib } # ----------------------------- diff --git a/readme.md b/readme.md index 873fc3bdf..caee505f7 100644 --- a/readme.md +++ b/readme.md @@ -55,9 +55,9 @@ I am assuming you used the installer from http://www.mingw.org/ #### Linux -Install appropriate libs as required. For ubuntu these are: libx11-dev, libxi-dev, libgl1-mesa-dev, libcurl4-gnutls-dev or libcurl4-openssl-dev +Install appropriate libs as required. For ubuntu these are: libx11-dev, libxi-dev and libgl1-mesa-dev -```gcc *.c -o ClassiCube -lm -lpthread -lX11 -lXi -lGL -lcurl -ldl``` +```gcc *.c -o ClassiCube -lm -lpthread -lX11 -lXi -lGL -ldl``` ##### Cross compiling for windows: @@ -66,37 +66,37 @@ Install appropriate libs as required. For ubuntu these are: libx11-dev, libxi-de ##### Raspberry pi Although the regular linux compiliation flags will work fine, to take full advantage of the hardware: -```gcc *.c -o ClassiCube -DCC_BUILD_RPI -lm -lpthread -lX11 -lEGL -lGLESv2 -lcurl -ldl``` +```gcc *.c -o ClassiCube -DCC_BUILD_RPI -lm -lpthread -lX11 -lEGL -lGLESv2 -ldl``` #### macOS (32 bit) -```gcc *.c -o ClassiCube -framework Carbon -framework AGL -framework OpenGL -lcurl``` +```gcc *.c -o ClassiCube -framework Carbon -framework AGL -framework OpenGL``` #### macOS (64 bit) -```gcc *.c -o ClassiCube -framework Cocoa -framework OpenGL -lcurl -lobjc``` +```gcc *.c -o ClassiCube -framework Cocoa -framework OpenGL -lobjc``` #### FreeBSD -```clang *.c -o ClassiCube -I /usr/local/include -L /usr/local/lib -lm -lpthread -lX11 -lXi -lGL -lcurl -lexecinfo``` +```clang *.c -o ClassiCube -I /usr/local/include -L /usr/local/lib -lm -lpthread -lX11 -lXi -lGL -lexecinfo``` #### OpenBSD Install libexecinfo package if needed. -```gcc *.c -o ClassiCube -I /usr/X11R6/include -I /usr/local/include -L /usr/X11R6/lib -L /usr/local/lib -lX11 -lXi -lGL -lcurl -lexecinfo``` +```gcc *.c -o ClassiCube -I /usr/X11R6/include -I /usr/local/include -L /usr/X11R6/lib -L /usr/local/lib -lX11 -lXi -lGL -lexecinfo``` #### NetBSD -```gcc *.c -o ClassiCube -I /usr/X11R7/include -I /usr/pkg/include -L /usr/X11R7/lib -L /usr/pkg/lib -lpthread -lX11 -lXi -lGL -lcurl -lexecinfo``` +```gcc *.c -o ClassiCube -I /usr/X11R7/include -I /usr/pkg/include -L /usr/X11R7/lib -L /usr/pkg/lib -lpthread -lX11 -lXi -lGL -lexecinfo``` #### DragonflyBSD -```gcc *.c -o ClassiCube -I /usr/local/include -L /usr/local/lib -lm -lpthread -lX11 -lXi -lGL -lcurl -lexecinfo``` +```gcc *.c -o ClassiCube -I /usr/local/include -L /usr/local/lib -lm -lpthread -lX11 -lXi -lGL -lexecinfo``` #### Solaris -```gcc *.c -o ClassiCube -lm -lsocket -lX11 -lXi -lGL -lcurl``` +```gcc *.c -o ClassiCube -lm -lsocket -lX11 -lXi -lGL``` NOTE: You have to change entry->d_type == DT_DIR to Directory_Exists(&path) (TODO do this automatically) @@ -104,7 +104,7 @@ NOTE: You have to change entry->d_type == DT_DIR to Directory_Exists(&path) (TOD Install libsdl2_devel, openal_devel, and libexecinfo_devel package if needed. -```gcc *.c -o ClassiCube -lm -lcurl -lexecinfo -lGL -lnetwork -lSDL2``` +```gcc *.c -o ClassiCube -lm -lexecinfo -lGL -lnetwork -lSDL2``` NOTE: You have to change entry->d_type == DT_DIR to Directory_Exists(&path) (TODO do this automatically) diff --git a/src/Builder.c b/src/Builder.c index b64057ec1..e0ea592ca 100644 --- a/src/Builder.c +++ b/src/Builder.c @@ -105,7 +105,7 @@ static void BuildPartVbs(struct ChunkPartInfo* info) { count = info->Counts[i]; if (count) { - info->Vbs[i] = Gfx_CreateVb2(&Builder_Vertices[offset], VERTEX_FORMAT_P3FT2FC4B, count); + info->Vbs[i] = Gfx_CreateVb2(&Builder_Vertices[offset], VERTEX_FORMAT_TEXTURED, count); offset += count; } else { info->Vbs[i] = 0; @@ -115,7 +115,7 @@ static void BuildPartVbs(struct ChunkPartInfo* info) { count = info->SpriteCount; offset = info->Offset; if (count) { - info->Vbs[i] = Gfx_CreateVb2(&Builder_Vertices[offset], VERTEX_FORMAT_P3FT2FC4B, count); + info->Vbs[i] = Gfx_CreateVb2(&Builder_Vertices[offset], VERTEX_FORMAT_TEXTURED, count); } else { info->Vbs[i] = 0; } @@ -398,7 +398,7 @@ static cc_bool BuildChunk(int x1, int y1, int z1, struct ChunkInfo* info) { #else /* NOTE: Relies on assumption vb is ignored by GL11 Gfx_LockVb implementation */ Builder_Vertices = (struct VertexTextured*)Gfx_LockVb(0, - VERTEX_FORMAT_P3FT2FC4B, totalVerts + 1); + VERTEX_FORMAT_TEXTURED, totalVerts + 1); #endif Builder_PostStretchTiles(); diff --git a/src/Gui.c b/src/Gui.c index 3896f8d51..38bdea3cd 100644 --- a/src/Gui.c +++ b/src/Gui.c @@ -410,4 +410,4 @@ struct IGameComponent Gui_Component = { Gui_Reset, /* Reset */ NULL, /* OnNewMap */ NULL, /* OnNewMapLoaded */ -}; \ No newline at end of file +}; diff --git a/src/Http.c b/src/Http.c index 40ec58846..94dde66ea 100644 --- a/src/Http.c +++ b/src/Http.c @@ -26,9 +26,6 @@ #elif defined CC_BUILD_WEB /* Use fetch/XMLHttpRequest api for Emscripten */ #include -#elif defined CC_BUILD_CURL -#include -#include #endif void HttpRequest_Free(struct HttpRequest* request) { @@ -652,10 +649,90 @@ static void Http_SysFree(void) { InternetCloseHandle(hInternet); } #elif defined CC_BUILD_CURL +#include "Errors.h" +#include +typedef void CURL; +struct curl_slist; +typedef int CURLcode; + +#define CURL_GLOBAL_DEFAULT ((1<<0) | (1<<1)) /* SSL and Win32 options */ +#define CURLOPT_WRITEDATA (10000 + 1) +#define CURLOPT_URL (10000 + 2) +#define CURLOPT_ERRORBUFFER (10000 + 10) +#define CURLOPT_WRITEFUNCTION (20000 + 11) +#define CURLOPT_POSTFIELDS (10000 + 15) +#define CURLOPT_USERAGENT (10000 + 18) +#define CURLOPT_HTTPHEADER (10000 + 23) +#define CURLOPT_HEADERDATA (10000 + 29) +#define CURLOPT_VERBOSE (0 + 41) +#define CURLOPT_HEADER (0 + 42) +#define CURLOPT_NOBODY (0 + 44) +#define CURLOPT_POST (0 + 47) +#define CURLOPT_FOLLOWLOCATION (0 + 52) +#define CURLOPT_POSTFIELDSIZE (0 + 60) +#define CURLOPT_MAXREDIRS (0 + 68) +#define CURLOPT_HEADERFUNCTION (20000 + 79) +#define CURLOPT_HTTPGET (0 + 80) + +#if defined _WIN32 +#define APIENTRY __cdecl +#else +#define APIENTRY +#endif + +typedef CURLcode (APIENTRY *FP_curl_global_init)(long flags); static FP_curl_global_init _curl_global_init; +typedef void (APIENTRY *FP_curl_global_cleanup)(void); static FP_curl_global_cleanup _curl_global_cleanup; +typedef CURL* (APIENTRY *FP_curl_easy_init)(void); static FP_curl_easy_init _curl_easy_init; +typedef CURLcode (APIENTRY *FP_curl_easy_perform)(CURL *c); static FP_curl_easy_perform _curl_easy_perform; +typedef CURLcode (APIENTRY *FP_curl_easy_setopt)(CURL *c, int opt, ...); static FP_curl_easy_setopt _curl_easy_setopt; +typedef void (APIENTRY *FP_curl_easy_cleanup)(CURL* c); static FP_curl_easy_cleanup _curl_easy_cleanup; +typedef const char* (APIENTRY *FP_curl_easy_strerror)(CURLcode res); static FP_curl_easy_strerror _curl_easy_strerror; +typedef void (APIENTRY *FP_curl_slist_free_all)(struct curl_slist* l); static FP_curl_slist_free_all _curl_slist_free_all; +typedef struct curl_slist* (APIENTRY *FP_curl_slist_append)(struct curl_slist* l, const char* v); static FP_curl_slist_append _curl_slist_append; +/* End of curl headers */ + +#if defined CC_BUILD_WIN +static const String curlLib = String_FromConst("libcurl.dll"); +static const String curlAlt = String_FromConst("curl.dll"); +#elif defined CC_BUILD_OSX +static const String curlLib = String_FromConst("/usr/lib/libcurl.4.dylib"); +static const String curlAlt = String_FromConst("/usr/lib/libcurl.dylib"); +#elif defined CC_BUILD_OPENBSD +static const String curlLib = String_FromConst("libcurl.so.25.17"); +static const String curlAlt = String_FromConst("libcurl.so.25.16"); +#else +static const String curlLib = String_FromConst("libcurl.so.4"); +static const String curlAlt = String_FromConst("libcurl.so.3"); +#endif + +#define QUOTE(x) #x +#define LoadCurlFunc(sym) (_ ## sym = (FP_ ## sym)DynamicLib_Get2(lib, QUOTE(sym))) +static cc_bool LoadCurlFuncs(void) { + void* lib = DynamicLib_Load2(&curlLib); + if (!lib) { + Logger_DynamicLibWarn("loading", &curlLib); + + lib = DynamicLib_Load2(&curlAlt); + if (!lib) { Logger_DynamicLibWarn("loading", &curlAlt); return false; } + } + /* Non-essential function missing in older curl versions */ + LoadCurlFunc(curl_easy_strerror); + + return + LoadCurlFunc(curl_global_init) && LoadCurlFunc(curl_global_cleanup) && + LoadCurlFunc(curl_easy_init) && LoadCurlFunc(curl_easy_perform) && + LoadCurlFunc(curl_easy_setopt) && LoadCurlFunc(curl_easy_cleanup) && + LoadCurlFunc(curl_slist_free_all) && LoadCurlFunc(curl_slist_append); +} + static CURL* curl; +static cc_bool curlSupported; cc_bool Http_DescribeError(cc_result res, String* dst) { - const char* err = curl_easy_strerror((CURLcode)res); + const char* err; + + if (!_curl_easy_strerror) return false; + err = _curl_easy_strerror((CURLcode)res); if (!err) return false; String_AppendConst(dst, err); @@ -663,11 +740,16 @@ cc_bool Http_DescribeError(cc_result res, String* dst) { } static void Http_SysInit(void) { - CURLcode res = curl_global_init(CURL_GLOBAL_DEFAULT); - if (res) Logger_Abort2(res, "Failed to init curl"); + static const String msg = String_FromConst("Failed to init libcurl. All HTTP requests will therefore fail."); + CURLcode res; - curl = curl_easy_init(); - if (!curl) Logger_Abort("Failed to init easy curl"); + if (!LoadCurlFuncs()) { Logger_WarnFunc(&msg); return; } + res = _curl_global_init(CURL_GLOBAL_DEFAULT); + if (res) { Logger_SimpleWarn(res, "initing curl"); return; } + + curl = _curl_easy_init(); + if (!curl) { Logger_SimpleWarn(res, "initing curl_easy"); return; } + curlSupported = true; } static struct curl_slist* headers_list; @@ -677,7 +759,7 @@ static void Http_AddHeader(const char* key, const String* value) { String_Format2(&tmp, "%c: %s", key, value); tmp.buffer[tmp.length] = '\0'; - headers_list = curl_slist_append(headers_list, tmp.buffer); + headers_list = _curl_slist_append(headers_list, tmp.buffer); } /* Processes a HTTP header downloaded from the server */ @@ -707,14 +789,14 @@ static size_t Http_ProcessData(char *buffer, size_t size, size_t nitems, void* u /* Sets general curl options for a request */ static void Http_SetCurlOpts(struct HttpRequest* req) { - curl_easy_setopt(curl, CURLOPT_USERAGENT, GAME_APP_NAME); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 20L); + _curl_easy_setopt(curl, CURLOPT_USERAGENT, GAME_APP_NAME); + _curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + _curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 20L); - curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, Http_ProcessHeader); - curl_easy_setopt(curl, CURLOPT_HEADERDATA, req); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, Http_ProcessData); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, req); + _curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, Http_ProcessHeader); + _curl_easy_setopt(curl, CURLOPT_HEADERDATA, req); + _curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, Http_ProcessData); + _curl_easy_setopt(curl, CURLOPT_WRITEDATA, req); } static cc_result Http_SysDo(struct HttpRequest* req, String* url) { @@ -722,41 +804,49 @@ static cc_result Http_SysDo(struct HttpRequest* req, String* url) { void* post_data = req->data; CURLcode res; - curl_easy_reset(curl); + if (!curlSupported) { + HttpRequest_Free(req); + return ERR_NOT_SUPPORTED; + } + headers_list = NULL; Http_SetRequestHeaders(req); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers_list); + _curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers_list); Http_SetCurlOpts(req); Platform_ConvertString(urlStr, url); - curl_easy_setopt(curl, CURLOPT_URL, urlStr); + _curl_easy_setopt(curl, CURLOPT_URL, urlStr); if (req->requestType == REQUEST_TYPE_HEAD) { - curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); + _curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); } else if (req->requestType == REQUEST_TYPE_POST) { - curl_easy_setopt(curl, CURLOPT_POST, 1L); - curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, req->size); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req->data); + _curl_easy_setopt(curl, CURLOPT_POST, 1L); + _curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, req->size); + _curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req->data); /* per curl docs, we must persist POST data until request finishes */ req->data = NULL; HttpRequest_Free(req); + } else { + /* Undo POST/HEAD state */ + _curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); } bufferSize = 0; http_curProgress = ASYNC_PROGRESS_FETCHING_DATA; - res = curl_easy_perform(curl); + res = _curl_easy_perform(curl); http_curProgress = 100; - curl_slist_free_all(headers_list); + _curl_slist_free_all(headers_list); /* can free now that request has finished */ Mem_Free(post_data); return res; } static void Http_SysFree(void) { - curl_easy_cleanup(curl); - curl_global_cleanup(); + if (!curlSupported) return; + _curl_easy_cleanup(curl); + _curl_global_cleanup(); } #elif defined CC_BUILD_ANDROID struct HttpRequest* java_req; diff --git a/src/Makefile b/src/Makefile index ca487ba5b..6a05b1e61 100644 --- a/src/Makefile +++ b/src/Makefile @@ -30,15 +30,15 @@ LIBS=-mwindows -lws2_32 -lwininet -lwinmm -limagehlp -lcrypt32 -ld3d9 endif ifeq ($(PLAT),linux) -LIBS=-lX11 -lXi -lpthread -lGL -lm -ldl -lcurl +LIBS=-lX11 -lXi -lpthread -lGL -lm -ldl endif ifeq ($(PLAT),sunos) -LIBS=-lm -lsocket -lX11 -lXi -lGL -lcurl +LIBS=-lm -lsocket -lX11 -lXi -lGL endif ifeq ($(PLAT),darwin) -LIBS=-lcurl +LIBS= LDFLAGS=-rdynamic -framework Carbon -framework AGL -framework OpenGL endif @@ -46,31 +46,31 @@ ifeq ($(PLAT),freebsd) CC=clang CFLAGS=-g -pipe -rdynamic -I /usr/local/include -fno-math-errno LDFLAGS=-L /usr/local/lib -LIBS=-lcurl -lexecinfo -lGL -lX11 -lXi -lm -lpthread +LIBS=-lexecinfo -lGL -lX11 -lXi -lm -lpthread endif ifeq ($(PLAT),openbsd) CFLAGS=-g -pipe -rdynamic -I /usr/X11R6/include -I /usr/local/include -fno-math-errno LDFLAGS=-L /usr/X11R6/lib -L /usr/local/lib -LIBS=-lcurl -lexecinfo -lGL -lX11 -lXi +LIBS=-lexecinfo -lGL -lX11 -lXi endif ifeq ($(PLAT),netbsd) CFLAGS=-g -pipe -rdynamic -I /usr/X11R7/include -I /usr/pkg/include -fno-math-errno LDFLAGS=-L /usr/X11R7/lib -L /usr/pkg/lib -LIBS=-lcurl -lexecinfo -lGL -lX11 -lXi +LIBS=-lexecinfo -lGL -lX11 -lXi endif ifeq ($(PLAT),dragonfly) CFLAGS=-g -pipe -rdynamic -I /usr/local/include -fno-math-errno LDFLAGS=-L /usr/local/lib -LIBS=-lcurl -lexecinfo -lGL -lX11 -lXi -lm -lpthread +LIBS=-lexecinfo -lGL -lX11 -lXi -lm -lpthread endif ifeq ($(PLAT),haiku) CFLAGS=-g -pipe -fno-math-errno LDFLAGS=-g -LIBS=-lcurl -lm -lexecinfo -lGL -lnetwork -lSDL2 +LIBS=-lm -lexecinfo -lGL -lnetwork -lSDL2 endif ifeq ($(OS),Windows_NT) diff --git a/src/Platform.c b/src/Platform.c index afe2cea32..3e291e4cb 100644 --- a/src/Platform.c +++ b/src/Platform.c @@ -47,7 +47,6 @@ const cc_result ReturnCode_SocketWouldBlock = WSAEWOULDBLOCK; #include #include #include -#include #include #include #include @@ -1392,8 +1391,46 @@ cc_bool DynamicLib_DescribeError(String* dst) { void* DynamicLib_Load2(const String* path) { return NULL; } void* DynamicLib_Get2(void* lib, const char* name) { return NULL; } cc_bool DynamicLib_DescribeError(String* dst) { return false; } +#elif defined CC_BUILD_OSX && __ppc__ +/* TODO: Only do it for macOS < 10.4 */ +const String DynamicLib_Ext = String_FromConst(".dylib"); + +void* DynamicLib_Load2(const String* path) { + char str[NATIVE_STR_LEN]; + Platform_ConvertString(str, path); + return NSAddImage(str, NSADDIMAGE_OPTION_WITH_SEARCHING | + NSADDIMAGE_OPTION_RETURN_ON_ERROR); +} + +void* DynamicLib_Get2(void* lib, const char* name) { + String tmp; char tmpBuffer[128]; + NSSymbol sym; + String_InitArray_NT(tmp, tmpBuffer); + + /* NS linker api rquires symbols to have a _ prefix */ + String_Append(&tmp, '_'); + String_AppendConst(&tmp, name); + tmp.buffer[tmp.length] = '\0'; + + sym = NSLookupSymbolInImage(lib, tmp.buffer, NSLOOKUPSYMBOLINIMAGE_OPTION_BIND_NOW | + NSLOOKUPSYMBOLINIMAGE_OPTION_RETURN_ON_ERROR); + return sym ? NSAddressOfSymbol(sym) : NULL; +} + +cc_bool DynamicLib_DescribeError(String* dst) { + NSLinkEditErrors err = 0; + const char* name = ""; + const char* msg = ""; + int errNum = 0; + + NSLinkEditError(&err, &errNum, &name, &msg); + String_Format4(dst, "%c in %c (%i, sys %i)", msg, name, &err, &errNum); + return true; +} #elif defined CC_BUILD_POSIX +#include /* TODO: Should we use .bundle instead of .dylib? */ + #ifdef CC_BUILD_OSX const String DynamicLib_Ext = String_FromConst(".dylib"); #else