GCC Code Coverage Report


Directory: ./
File: submodules/raylib/src/utils.c
Date: 2023-09-29 04:53:15
Exec Total Coverage
Lines: 0 126 0.0%
Branches: 0 71 0.0%

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