]> git.sesse.net Git - pistorm/blob - raylib/utils.c
Add Meson build files.
[pistorm] / raylib / utils.c
1 /**********************************************************************************************
2 *
3 *   raylib.utils - Some common utility functions
4 *
5 *   CONFIGURATION:
6 *
7 *   #define SUPPORT_TRACELOG
8 *       Show TraceLog() output messages
9 *       NOTE: By default LOG_DEBUG traces not shown
10 *
11 *
12 *   LICENSE: zlib/libpng
13 *
14 *   Copyright (c) 2014-2021 Ramon Santamaria (@raysan5)
15 *
16 *   This software is provided "as-is", without any express or implied warranty. In no event
17 *   will the authors be held liable for any damages arising from the use of this software.
18 *
19 *   Permission is granted to anyone to use this software for any purpose, including commercial
20 *   applications, and to alter it and redistribute it freely, subject to the following restrictions:
21 *
22 *     1. The origin of this software must not be misrepresented; you must not claim that you
23 *     wrote the original software. If you use this software in a product, an acknowledgment
24 *     in the product documentation would be appreciated but is not required.
25 *
26 *     2. Altered source versions must be plainly marked as such, and must not be misrepresented
27 *     as being the original software.
28 *
29 *     3. This notice may not be removed or altered from any source distribution.
30 *
31 **********************************************************************************************/
32
33 #include "raylib.h"                     // WARNING: Required for: LogType enum
34
35 // Check if config flags have been externally provided on compilation line
36 #if !defined(EXTERNAL_CONFIG_FLAGS)
37     #include "config.h"                 // Defines module configuration flags
38 #endif
39
40 #include "utils.h"
41
42 #if defined(PLATFORM_ANDROID)
43     #include <errno.h>                  // Required for: Android error types
44     #include <android/log.h>            // Required for: Android log system: __android_log_vprint()
45     #include <android/asset_manager.h>  // Required for: Android assets manager: AAsset, AAssetManager_open(), ...
46 #endif
47
48 #include <stdlib.h>                     // Required for: exit()
49 #include <stdio.h>                      // Required for: FILE, fopen(), fseek(), ftell(), fread(), fwrite(), fprintf(), vprintf(), fclose()
50 #include <stdarg.h>                     // Required for: va_list, va_start(), va_end()
51 #include <string.h>                     // Required for: strcpy(), strcat()
52
53 //----------------------------------------------------------------------------------
54 // Defines and Macros
55 //----------------------------------------------------------------------------------
56 #ifndef MAX_TRACELOG_MSG_LENGTH
57     #define MAX_TRACELOG_MSG_LENGTH     128     // Max length of one trace-log message
58 #endif
59 #ifndef MAX_UWP_MESSAGES
60     #define MAX_UWP_MESSAGES            512     // Max UWP messages to process
61 #endif
62
63 //----------------------------------------------------------------------------------
64 // Global Variables Definition
65 //----------------------------------------------------------------------------------
66 static int logTypeLevel = LOG_INFO;                 // Minimum log type level
67
68 static TraceLogCallback traceLog = NULL;            // TraceLog callback function pointer
69 static LoadFileDataCallback loadFileData = NULL;    // LoadFileData callback funtion pointer
70 static SaveFileDataCallback saveFileData = NULL;    // SaveFileText callback funtion pointer
71 static LoadFileTextCallback loadFileText = NULL;    // LoadFileText callback funtion pointer
72 static SaveFileTextCallback saveFileText = NULL;    // SaveFileText callback funtion pointer
73
74 //----------------------------------------------------------------------------------
75 // Functions to set internal callbacks
76 //----------------------------------------------------------------------------------
77 void SetTraceLogCallback(TraceLogCallback callback) { traceLog = callback; }              // Set custom trace log
78 void SetLoadFileDataCallback(LoadFileDataCallback callback) { loadFileData = callback; }  // Set custom file data loader
79 void SetSaveFileDataCallback(SaveFileDataCallback callback) { saveFileData = callback; }  // Set custom file data saver
80 void SetLoadFileTextCallback(LoadFileTextCallback callback) { loadFileText = callback; }  // Set custom file text loader
81 void SetSaveFileTextCallback(SaveFileTextCallback callback) { saveFileText = callback; }  // Set custom file text saver
82
83
84 #if defined(PLATFORM_ANDROID)
85 static AAssetManager *assetManager = NULL;              // Android assets manager pointer
86 static const char *internalDataPath = NULL;             // Android internal data path
87 #endif
88
89 //----------------------------------------------------------------------------------
90 // Module specific Functions Declaration
91 //----------------------------------------------------------------------------------
92 #if defined(PLATFORM_ANDROID)
93 FILE *funopen(const void *cookie, int (*readfn)(void *, char *, int), int (*writefn)(void *, const char *, int),
94               fpos_t (*seekfn)(void *, fpos_t, int), int (*closefn)(void *));
95
96 static int android_read(void *cookie, char *buf, int size);
97 static int android_write(void *cookie, const char *buf, int size);
98 static fpos_t android_seek(void *cookie, fpos_t offset, int whence);
99 static int android_close(void *cookie);
100 #endif
101
102 //----------------------------------------------------------------------------------
103 // Module Functions Definition - Utilities
104 //----------------------------------------------------------------------------------
105
106 // Set the current threshold (minimum) log level
107 void SetTraceLogLevel(int logType) { logTypeLevel = logType; }
108
109 // Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG)
110 void TraceLog(int logType, const char *text, ...)
111 {
112 #if defined(SUPPORT_TRACELOG)
113     // Message has level below current threshold, don't emit
114     if (logType < logTypeLevel) return;
115
116     va_list args;
117     va_start(args, text);
118
119     if (traceLog)
120     {
121         traceLog(logType, text, args);
122         va_end(args);
123         return;
124     }
125
126 #if defined(PLATFORM_ANDROID)
127     switch(logType)
128     {
129         case LOG_TRACE: __android_log_vprint(ANDROID_LOG_VERBOSE, "raylib", text, args); break;
130         case LOG_DEBUG: __android_log_vprint(ANDROID_LOG_DEBUG, "raylib", text, args); break;
131         case LOG_INFO: __android_log_vprint(ANDROID_LOG_INFO, "raylib", text, args); break;
132         case LOG_WARNING: __android_log_vprint(ANDROID_LOG_WARN, "raylib", text, args); break;
133         case LOG_ERROR: __android_log_vprint(ANDROID_LOG_ERROR, "raylib", text, args); break;
134         case LOG_FATAL: __android_log_vprint(ANDROID_LOG_FATAL, "raylib", text, args); break;
135         default: break;
136     }
137 #else
138     char buffer[MAX_TRACELOG_MSG_LENGTH] = { 0 };
139
140     switch (logType)
141     {
142         case LOG_TRACE: strcpy(buffer, "TRACE: "); break;
143         case LOG_DEBUG: strcpy(buffer, "DEBUG: "); break;
144         case LOG_INFO: strcpy(buffer, "INFO: "); break;
145         case LOG_WARNING: strcpy(buffer, "WARNING: "); break;
146         case LOG_ERROR: strcpy(buffer, "ERROR: "); break;
147         case LOG_FATAL: strcpy(buffer, "FATAL: "); break;
148         default: break;
149     }
150
151     strcat(buffer, text);
152     strcat(buffer, "\n");
153     vprintf(buffer, args);
154 #endif
155
156     va_end(args);
157
158     if (logType == LOG_ERROR) exit(1);  // If error, exit program
159
160 #endif  // SUPPORT_TRACELOG
161 }
162
163 // Internal memory allocator
164 // NOTE: Initializes to zero by default
165 void *MemAlloc(int size)
166 {
167     void *ptr = RL_CALLOC(size, 1);
168     return ptr;
169 }
170
171 // Internal memory reallocator
172 void *MemRealloc(void *ptr, int size)
173 {
174     void *ret = RL_REALLOC(ptr, size);
175     return ret;
176 }
177
178 // Internal memory free
179 void MemFree(void *ptr)
180 {
181     RL_FREE(ptr);
182 }
183
184 // Load data from file into a buffer
185 unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead)
186 {
187     unsigned char *data = NULL;
188     *bytesRead = 0;
189
190     if (fileName != NULL)
191     {
192         if (loadFileData)
193         {
194             data = loadFileData(fileName, bytesRead);
195             return data;
196         }
197 #if defined(SUPPORT_STANDARD_FILEIO)
198         FILE *file = fopen(fileName, "rb");
199
200         if (file != NULL)
201         {
202             // WARNING: On binary streams SEEK_END could not be found,
203             // using fseek() and ftell() could not work in some (rare) cases
204             fseek(file, 0, SEEK_END);
205             int size = ftell(file);
206             fseek(file, 0, SEEK_SET);
207
208             if (size > 0)
209             {
210                 data = (unsigned char *)RL_MALLOC(size*sizeof(unsigned char));
211
212                 // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements]
213                 unsigned int count = (unsigned int)fread(data, sizeof(unsigned char), size, file);
214                 *bytesRead = count;
215
216                 if (count != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded", fileName);
217                 else TRACELOG(LOG_INFO, "FILEIO: [%s] File loaded successfully", fileName);
218             }
219             else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to read file", fileName);
220
221             fclose(file);
222         }
223         else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName);
224 #else
225     TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback");
226 #endif
227     }
228     else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
229
230     return data;
231 }
232
233 // Unload file data allocated by LoadFileData()
234 void UnloadFileData(unsigned char *data)
235 {
236     RL_FREE(data);
237 }
238
239 // Save data to file from buffer
240 bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite)
241 {
242     bool success = false;
243
244     if (fileName != NULL)
245     {
246         if (saveFileData)
247         {
248             return saveFileData(fileName, data, bytesToWrite);
249         }
250 #if defined(SUPPORT_STANDARD_FILEIO)
251         FILE *file = fopen(fileName, "wb");
252
253         if (file != NULL)
254         {
255             unsigned int count = (unsigned int)fwrite(data, sizeof(unsigned char), bytesToWrite, file);
256
257             if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write file", fileName);
258             else if (count != bytesToWrite) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", fileName);
259             else TRACELOG(LOG_INFO, "FILEIO: [%s] File saved successfully", fileName);
260
261             int result = fclose(file);
262             if (result == 0) success = true;
263         }
264         else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName);
265 #else
266     TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback");
267 #endif
268     }
269     else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
270
271     return success;
272 }
273
274 // Load text data from file, returns a '\0' terminated string
275 // NOTE: text chars array should be freed manually
276 char *LoadFileText(const char *fileName)
277 {
278     char *text = NULL;
279
280     if (fileName != NULL)
281     {
282         if (loadFileText)
283         {
284             text = loadFileText(fileName);
285             return text;
286         }
287 #if defined(SUPPORT_STANDARD_FILEIO)
288         FILE *file = fopen(fileName, "rt");
289
290         if (file != NULL)
291         {
292             // WARNING: When reading a file as 'text' file,
293             // text mode causes carriage return-linefeed translation...
294             // ...but using fseek() should return correct byte-offset
295             fseek(file, 0, SEEK_END);
296             unsigned int size = (unsigned int)ftell(file);
297             fseek(file, 0, SEEK_SET);
298
299             if (size > 0)
300             {
301                 text = (char *)RL_MALLOC((size + 1)*sizeof(char));
302                 unsigned int count = (unsigned int)fread(text, sizeof(char), size, file);
303
304                 // WARNING: \r\n is converted to \n on reading, so,
305                 // read bytes count gets reduced by the number of lines
306                 if (count < size) text = RL_REALLOC(text, count + 1);
307
308                 // Zero-terminate the string
309                 text[count] = '\0';
310
311                 TRACELOG(LOG_INFO, "FILEIO: [%s] Text file loaded successfully", fileName);
312             }
313             else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to read text file", fileName);
314
315             fclose(file);
316         }
317         else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName);
318 #else
319     TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback");
320 #endif
321     }
322     else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
323
324     return text;
325 }
326
327 // Unload file text data allocated by LoadFileText()
328 void UnloadFileText(unsigned char *text)
329 {
330     RL_FREE(text);
331 }
332
333 // Save text data to file (write), string must be '\0' terminated
334 bool SaveFileText(const char *fileName, char *text)
335 {
336     bool success = false;
337
338     if (fileName != NULL)
339     {
340         if (saveFileText)
341         {
342             return saveFileText(fileName, text);
343         }
344 #if defined(SUPPORT_STANDARD_FILEIO)
345         FILE *file = fopen(fileName, "wt");
346
347         if (file != NULL)
348         {
349             int count = fprintf(file, "%s", text);
350
351             if (count < 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write text file", fileName);
352             else TRACELOG(LOG_INFO, "FILEIO: [%s] Text file saved successfully", fileName);
353
354             int result = fclose(file);
355             if (result == 0) success = true;
356         }
357         else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName);
358 #else
359     TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback");
360 #endif
361     }
362     else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
363
364     return success;
365 }
366
367 #if defined(PLATFORM_ANDROID)
368 // Initialize asset manager from android app
369 void InitAssetManager(AAssetManager *manager, const char *dataPath)
370 {
371     assetManager = manager;
372     internalDataPath = dataPath;
373 }
374
375 // Replacement for fopen()
376 // Ref: https://developer.android.com/ndk/reference/group/asset
377 FILE *android_fopen(const char *fileName, const char *mode)
378 {
379     if (mode[0] == 'w')
380     {
381         // TODO: fopen() is mapped to android_fopen() that only grants read access
382         // to assets directory through AAssetManager but we want to also be able to
383         // write data when required using the standard stdio FILE access functions
384         // Ref: https://stackoverflow.com/questions/11294487/android-writing-saving-files-from-native-code-only
385         #undef fopen
386         return fopen(TextFormat("%s/%s", internalDataPath, fileName), mode);
387         #define fopen(name, mode) android_fopen(name, mode)
388     }
389     else
390     {
391         // NOTE: AAsset provides access to read-only asset
392         AAsset *asset = AAssetManager_open(assetManager, fileName, AASSET_MODE_UNKNOWN);
393
394         if (asset != NULL)
395         {
396             // Return pointer to file in the assets
397             return funopen(asset, android_read, android_write, android_seek, android_close);
398         }
399         else
400         {
401             #undef fopen
402             // Just do a regular open if file is not found in the assets
403             return fopen(TextFormat("%s/%s", internalDataPath, fileName), mode);
404             #define fopen(name, mode) android_fopen(name, mode)
405         }
406     }
407 }
408 #endif  // PLATFORM_ANDROID
409
410 //----------------------------------------------------------------------------------
411 // Module specific Functions Definition
412 //----------------------------------------------------------------------------------
413 #if defined(PLATFORM_ANDROID)
414 static int android_read(void *cookie, char *buf, int size)
415 {
416     return AAsset_read((AAsset *)cookie, buf, size);
417 }
418
419 static int android_write(void *cookie, const char *buf, int size)
420 {
421     TRACELOG(LOG_WARNING, "ANDROID: Failed to provide write access to APK");
422
423     return EACCES;
424 }
425
426 static fpos_t android_seek(void *cookie, fpos_t offset, int whence)
427 {
428     return AAsset_seek((AAsset *)cookie, offset, whence);
429 }
430
431 static int android_close(void *cookie)
432 {
433     AAsset_close((AAsset *)cookie);
434     return 0;
435 }
436 #endif  // PLATFORM_ANDROID