utils.c (20792B)
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-2024 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 #if defined(PLATFORM_ANDROID) 80 static AAssetManager *assetManager = NULL; // Android assets manager pointer 81 static const char *internalDataPath = NULL; // Android internal data path 82 #endif 83 84 //---------------------------------------------------------------------------------- 85 // Module specific Functions Declaration 86 //---------------------------------------------------------------------------------- 87 #if defined(PLATFORM_ANDROID) 88 FILE *funopen(const void *cookie, int (*readfn)(void *, char *, int), int (*writefn)(void *, const char *, int), 89 fpos_t (*seekfn)(void *, fpos_t, int), int (*closefn)(void *)); 90 91 static int android_read(void *cookie, char *buf, int size); 92 static int android_write(void *cookie, const char *buf, int size); 93 static fpos_t android_seek(void *cookie, fpos_t offset, int whence); 94 static int android_close(void *cookie); 95 #endif 96 97 //---------------------------------------------------------------------------------- 98 // Module Functions Definition - Utilities 99 //---------------------------------------------------------------------------------- 100 101 // Set the current threshold (minimum) log level 102 void SetTraceLogLevel(int logType) { logTypeLevel = logType; } 103 104 // Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG) 105 void TraceLog(int logType, const char *text, ...) 106 { 107 #if defined(SUPPORT_TRACELOG) 108 // Message has level below current threshold, don't emit 109 if (logType < logTypeLevel) return; 110 111 va_list args; 112 va_start(args, text); 113 114 if (traceLog) 115 { 116 traceLog(logType, text, args); 117 va_end(args); 118 return; 119 } 120 121 #if defined(PLATFORM_ANDROID) 122 switch (logType) 123 { 124 case LOG_TRACE: __android_log_vprint(ANDROID_LOG_VERBOSE, "raylib", text, args); break; 125 case LOG_DEBUG: __android_log_vprint(ANDROID_LOG_DEBUG, "raylib", text, args); break; 126 case LOG_INFO: __android_log_vprint(ANDROID_LOG_INFO, "raylib", text, args); break; 127 case LOG_WARNING: __android_log_vprint(ANDROID_LOG_WARN, "raylib", text, args); break; 128 case LOG_ERROR: __android_log_vprint(ANDROID_LOG_ERROR, "raylib", text, args); break; 129 case LOG_FATAL: __android_log_vprint(ANDROID_LOG_FATAL, "raylib", text, args); break; 130 default: break; 131 } 132 #else 133 char buffer[MAX_TRACELOG_MSG_LENGTH] = { 0 }; 134 135 switch (logType) 136 { 137 case LOG_TRACE: strcpy(buffer, "TRACE: "); break; 138 case LOG_DEBUG: strcpy(buffer, "DEBUG: "); break; 139 case LOG_INFO: strcpy(buffer, "INFO: "); break; 140 case LOG_WARNING: strcpy(buffer, "WARNING: "); break; 141 case LOG_ERROR: strcpy(buffer, "ERROR: "); break; 142 case LOG_FATAL: strcpy(buffer, "FATAL: "); break; 143 default: break; 144 } 145 146 unsigned int textSize = (unsigned int)strlen(text); 147 memcpy(buffer + strlen(buffer), text, (textSize < (MAX_TRACELOG_MSG_LENGTH - 12))? textSize : (MAX_TRACELOG_MSG_LENGTH - 12)); 148 strcat(buffer, "\n"); 149 vprintf(buffer, args); 150 fflush(stdout); 151 #endif 152 153 va_end(args); 154 155 if (logType == LOG_FATAL) exit(EXIT_FAILURE); // If fatal logging, exit program 156 157 #endif // SUPPORT_TRACELOG 158 } 159 160 // Internal memory allocator 161 // NOTE: Initializes to zero by default 162 void *MemAlloc(unsigned int size) 163 { 164 void *ptr = RL_CALLOC(size, 1); 165 return ptr; 166 } 167 168 // Internal memory reallocator 169 void *MemRealloc(void *ptr, unsigned int size) 170 { 171 void *ret = RL_REALLOC(ptr, size); 172 return ret; 173 } 174 175 // Internal memory free 176 void MemFree(void *ptr) 177 { 178 RL_FREE(ptr); 179 } 180 181 // Load data from file into a buffer 182 unsigned char *LoadFileData(const char *fileName, int *dataSize) 183 { 184 unsigned char *data = NULL; 185 *dataSize = 0; 186 187 if (fileName != NULL) 188 { 189 if (loadFileData) 190 { 191 data = loadFileData(fileName, dataSize); 192 return data; 193 } 194 #if defined(SUPPORT_STANDARD_FILEIO) 195 FILE *file = fopen(fileName, "rb"); 196 197 if (file != NULL) 198 { 199 // WARNING: On binary streams SEEK_END could not be found, 200 // using fseek() and ftell() could not work in some (rare) cases 201 fseek(file, 0, SEEK_END); 202 int size = ftell(file); // WARNING: ftell() returns 'long int', maximum size returned is INT_MAX (2147483647 bytes) 203 fseek(file, 0, SEEK_SET); 204 205 if (size > 0) 206 { 207 data = (unsigned char *)RL_MALLOC(size*sizeof(unsigned char)); 208 209 if (data != NULL) 210 { 211 // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements] 212 size_t count = fread(data, sizeof(unsigned char), size, file); 213 214 // WARNING: fread() returns a size_t value, usually 'unsigned int' (32bit compilation) and 'unsigned long long' (64bit compilation) 215 // dataSize is unified along raylib as a 'int' type, so, for file-sizes > INT_MAX (2147483647 bytes) we have a limitation 216 if (count > 2147483647) 217 { 218 TRACELOG(LOG_WARNING, "FILEIO: [%s] File is bigger than 2147483647 bytes, avoid using LoadFileData()", fileName); 219 220 RL_FREE(data); 221 data = NULL; 222 } 223 else 224 { 225 *dataSize = (int)count; 226 227 if ((*dataSize) != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded (%i bytes out of %i)", fileName, dataSize, count); 228 else TRACELOG(LOG_INFO, "FILEIO: [%s] File loaded successfully", fileName); 229 } 230 } 231 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to allocated memory for file reading", fileName); 232 } 233 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to read file", fileName); 234 235 fclose(file); 236 } 237 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName); 238 #else 239 TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback"); 240 #endif 241 } 242 else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); 243 244 return data; 245 } 246 247 // Unload file data allocated by LoadFileData() 248 void UnloadFileData(unsigned char *data) 249 { 250 RL_FREE(data); 251 } 252 253 // Save data to file from buffer 254 bool SaveFileData(const char *fileName, void *data, int dataSize) 255 { 256 bool success = false; 257 258 if (fileName != NULL) 259 { 260 if (saveFileData) 261 { 262 return saveFileData(fileName, data, dataSize); 263 } 264 #if defined(SUPPORT_STANDARD_FILEIO) 265 FILE *file = fopen(fileName, "wb"); 266 267 if (file != NULL) 268 { 269 // WARNING: fwrite() returns a size_t value, usually 'unsigned int' (32bit compilation) and 'unsigned long long' (64bit compilation) 270 // and expects a size_t input value but as dataSize is limited to INT_MAX (2147483647 bytes), there shouldn't be a problem 271 int count = (int)fwrite(data, sizeof(unsigned char), dataSize, file); 272 273 if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write file", fileName); 274 else if (count != dataSize) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", fileName); 275 else TRACELOG(LOG_INFO, "FILEIO: [%s] File saved successfully", fileName); 276 277 int result = fclose(file); 278 if (result == 0) success = true; 279 } 280 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName); 281 #else 282 TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback"); 283 #endif 284 } 285 else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); 286 287 return success; 288 } 289 290 // Export data to code (.h), returns true on success 291 bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileName) 292 { 293 bool success = false; 294 295 #ifndef TEXT_BYTES_PER_LINE 296 #define TEXT_BYTES_PER_LINE 20 297 #endif 298 299 // NOTE: Text data buffer size is estimated considering raw data size in bytes 300 // and requiring 6 char bytes for every byte: "0x00, " 301 char *txtData = (char *)RL_CALLOC(dataSize*6 + 2000, sizeof(char)); 302 303 int byteCount = 0; 304 byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n"); 305 byteCount += sprintf(txtData + byteCount, "// //\n"); 306 byteCount += sprintf(txtData + byteCount, "// DataAsCode exporter v1.0 - Raw data exported as an array of bytes //\n"); 307 byteCount += sprintf(txtData + byteCount, "// //\n"); 308 byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n"); 309 byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n"); 310 byteCount += sprintf(txtData + byteCount, "// //\n"); 311 byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2022-2024 Ramon Santamaria (@raysan5) //\n"); 312 byteCount += sprintf(txtData + byteCount, "// //\n"); 313 byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n"); 314 315 // Get file name from path 316 char varFileName[256] = { 0 }; 317 strcpy(varFileName, GetFileNameWithoutExt(fileName)); 318 for (int i = 0; varFileName[i] != '\0'; i++) 319 { 320 // Convert variable name to uppercase 321 if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; } 322 // Replace non valid character for C identifier with '_' 323 else if (varFileName[i] == '.' || varFileName[i] == '-' || varFileName[i] == '?' || varFileName[i] == '!' || varFileName[i] == '+') { varFileName[i] = '_'; } 324 } 325 326 byteCount += sprintf(txtData + byteCount, "#define %s_DATA_SIZE %i\n\n", varFileName, dataSize); 327 328 byteCount += sprintf(txtData + byteCount, "static unsigned char %s_DATA[%s_DATA_SIZE] = { ", varFileName, varFileName); 329 for (int i = 0; i < (dataSize - 1); i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), data[i]); 330 byteCount += sprintf(txtData + byteCount, "0x%x };\n", data[dataSize - 1]); 331 332 // NOTE: Text data size exported is determined by '\0' (NULL) character 333 success = SaveFileText(fileName, txtData); 334 335 RL_FREE(txtData); 336 337 if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Data as code exported successfully", fileName); 338 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export data as code", fileName); 339 340 return success; 341 } 342 343 // Load text data from file, returns a '\0' terminated string 344 // NOTE: text chars array should be freed manually 345 char *LoadFileText(const char *fileName) 346 { 347 char *text = NULL; 348 349 if (fileName != NULL) 350 { 351 if (loadFileText) 352 { 353 text = loadFileText(fileName); 354 return text; 355 } 356 #if defined(SUPPORT_STANDARD_FILEIO) 357 FILE *file = fopen(fileName, "rt"); 358 359 if (file != NULL) 360 { 361 // WARNING: When reading a file as 'text' file, 362 // text mode causes carriage return-linefeed translation... 363 // ...but using fseek() should return correct byte-offset 364 fseek(file, 0, SEEK_END); 365 unsigned int size = (unsigned int)ftell(file); 366 fseek(file, 0, SEEK_SET); 367 368 if (size > 0) 369 { 370 text = (char *)RL_MALLOC((size + 1)*sizeof(char)); 371 372 if (text != NULL) 373 { 374 unsigned int count = (unsigned int)fread(text, sizeof(char), size, file); 375 376 // WARNING: \r\n is converted to \n on reading, so, 377 // read bytes count gets reduced by the number of lines 378 if (count < size) text = RL_REALLOC(text, count + 1); 379 380 // Zero-terminate the string 381 text[count] = '\0'; 382 383 TRACELOG(LOG_INFO, "FILEIO: [%s] Text file loaded successfully", fileName); 384 } 385 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to allocated memory for file reading", fileName); 386 } 387 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to read text file", fileName); 388 389 fclose(file); 390 } 391 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName); 392 #else 393 TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback"); 394 #endif 395 } 396 else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); 397 398 return text; 399 } 400 401 // Unload file text data allocated by LoadFileText() 402 void UnloadFileText(char *text) 403 { 404 RL_FREE(text); 405 } 406 407 // Save text data to file (write), string must be '\0' terminated 408 bool SaveFileText(const char *fileName, char *text) 409 { 410 bool success = false; 411 412 if (fileName != NULL) 413 { 414 if (saveFileText) 415 { 416 return saveFileText(fileName, text); 417 } 418 #if defined(SUPPORT_STANDARD_FILEIO) 419 FILE *file = fopen(fileName, "wt"); 420 421 if (file != NULL) 422 { 423 int count = fprintf(file, "%s", text); 424 425 if (count < 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write text file", fileName); 426 else TRACELOG(LOG_INFO, "FILEIO: [%s] Text file saved successfully", fileName); 427 428 int result = fclose(file); 429 if (result == 0) success = true; 430 } 431 else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName); 432 #else 433 TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback"); 434 #endif 435 } 436 else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid"); 437 438 return success; 439 } 440 441 #if defined(PLATFORM_ANDROID) 442 // Initialize asset manager from android app 443 void InitAssetManager(AAssetManager *manager, const char *dataPath) 444 { 445 assetManager = manager; 446 internalDataPath = dataPath; 447 } 448 449 // Replacement for fopen() 450 // Ref: https://developer.android.com/ndk/reference/group/asset 451 FILE *android_fopen(const char *fileName, const char *mode) 452 { 453 if (mode[0] == 'w') 454 { 455 // fopen() is mapped to android_fopen() that only grants read access to 456 // assets directory through AAssetManager but we want to also be able to 457 // write data when required using the standard stdio FILE access functions 458 // Ref: https://stackoverflow.com/questions/11294487/android-writing-saving-files-from-native-code-only 459 #undef fopen 460 return fopen(TextFormat("%s/%s", internalDataPath, fileName), mode); 461 #define fopen(name, mode) android_fopen(name, mode) 462 } 463 else 464 { 465 // NOTE: AAsset provides access to read-only asset 466 AAsset *asset = AAssetManager_open(assetManager, fileName, AASSET_MODE_UNKNOWN); 467 468 if (asset != NULL) 469 { 470 // Get pointer to file in the assets 471 return funopen(asset, android_read, android_write, android_seek, android_close); 472 } 473 else 474 { 475 #undef fopen 476 // Just do a regular open if file is not found in the assets 477 return fopen(TextFormat("%s/%s", internalDataPath, fileName), mode); 478 #define fopen(name, mode) android_fopen(name, mode) 479 } 480 } 481 } 482 #endif // PLATFORM_ANDROID 483 484 //---------------------------------------------------------------------------------- 485 // Module specific Functions Definition 486 //---------------------------------------------------------------------------------- 487 #if defined(PLATFORM_ANDROID) 488 static int android_read(void *cookie, char *data, int dataSize) 489 { 490 return AAsset_read((AAsset *)cookie, data, dataSize); 491 } 492 493 static int android_write(void *cookie, const char *data, int dataSize) 494 { 495 TRACELOG(LOG_WARNING, "ANDROID: Failed to provide write access to APK"); 496 497 return EACCES; 498 } 499 500 static fpos_t android_seek(void *cookie, fpos_t offset, int whence) 501 { 502 return AAsset_seek((AAsset *)cookie, offset, whence); 503 } 504 505 static int android_close(void *cookie) 506 { 507 AAsset_close((AAsset *)cookie); 508 return 0; 509 } 510 #endif // PLATFORM_ANDROID