utils.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  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-2025 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. #include "raylib.h" // WARNING: Required for: LogType enum
  32. // Check if config flags have been externally provided on compilation line
  33. #if !defined(EXTERNAL_CONFIG_FLAGS)
  34. #include "config.h" // Defines module configuration flags
  35. #endif
  36. #include "utils.h"
  37. #if defined(PLATFORM_ANDROID)
  38. #include <errno.h> // Required for: Android error types
  39. #include <android/log.h> // Required for: Android log system: __android_log_vprint()
  40. #include <android/asset_manager.h> // Required for: Android assets manager: AAsset, AAssetManager_open()...
  41. #endif
  42. #include <stdlib.h> // Required for: exit()
  43. #include <stdio.h> // Required for: FILE, fopen(), fseek(), ftell(), fread(), fwrite(), fprintf(), vprintf(), fclose()
  44. #include <stdarg.h> // Required for: va_list, va_start(), va_end()
  45. #include <string.h> // Required for: strcpy(), strcat()
  46. //----------------------------------------------------------------------------------
  47. // Defines and Macros
  48. //----------------------------------------------------------------------------------
  49. #ifndef MAX_TRACELOG_MSG_LENGTH
  50. #define MAX_TRACELOG_MSG_LENGTH 256 // Max length of one trace-log message
  51. #endif
  52. //----------------------------------------------------------------------------------
  53. // Global Variables Definition
  54. //----------------------------------------------------------------------------------
  55. static int logTypeLevel = LOG_INFO; // Minimum log type level
  56. static TraceLogCallback traceLog = NULL; // TraceLog callback function pointer
  57. static LoadFileDataCallback loadFileData = NULL; // LoadFileData callback function pointer
  58. static SaveFileDataCallback saveFileData = NULL; // SaveFileText callback function pointer
  59. static LoadFileTextCallback loadFileText = NULL; // LoadFileText callback function pointer
  60. static SaveFileTextCallback saveFileText = NULL; // SaveFileText callback function pointer
  61. //----------------------------------------------------------------------------------
  62. // Functions to set internal callbacks
  63. //----------------------------------------------------------------------------------
  64. void SetTraceLogCallback(TraceLogCallback callback) { traceLog = callback; } // Set custom trace log
  65. void SetLoadFileDataCallback(LoadFileDataCallback callback) { loadFileData = callback; } // Set custom file data loader
  66. void SetSaveFileDataCallback(SaveFileDataCallback callback) { saveFileData = callback; } // Set custom file data saver
  67. void SetLoadFileTextCallback(LoadFileTextCallback callback) { loadFileText = callback; } // Set custom file text loader
  68. void SetSaveFileTextCallback(SaveFileTextCallback callback) { saveFileText = callback; } // Set custom file text saver
  69. #if defined(PLATFORM_ANDROID)
  70. static AAssetManager *assetManager = NULL; // Android assets manager pointer
  71. static const char *internalDataPath = NULL; // Android internal data path
  72. #endif
  73. //----------------------------------------------------------------------------------
  74. // Module Internal Functions Declaration
  75. //----------------------------------------------------------------------------------
  76. #if defined(PLATFORM_ANDROID)
  77. FILE *funopen(const void *cookie, int (*readfn)(void *, char *, int), int (*writefn)(void *, const char *, int),
  78. fpos_t (*seekfn)(void *, fpos_t, int), int (*closefn)(void *));
  79. static int android_read(void *cookie, char *buf, int size);
  80. static int android_write(void *cookie, const char *buf, int size);
  81. static fpos_t android_seek(void *cookie, fpos_t offset, int whence);
  82. static int android_close(void *cookie);
  83. #endif
  84. //----------------------------------------------------------------------------------
  85. // Module Functions Definition
  86. //----------------------------------------------------------------------------------
  87. // Set the current threshold (minimum) log level
  88. void SetTraceLogLevel(int logType) { logTypeLevel = logType; }
  89. // Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG)
  90. void TraceLog(int logType, const char *text, ...)
  91. {
  92. #if defined(SUPPORT_TRACELOG)
  93. // Message has level below current threshold, don't emit
  94. if (logType < logTypeLevel) return;
  95. va_list args;
  96. va_start(args, text);
  97. if (traceLog)
  98. {
  99. traceLog(logType, text, args);
  100. va_end(args);
  101. return;
  102. }
  103. #if defined(PLATFORM_ANDROID)
  104. switch (logType)
  105. {
  106. case LOG_TRACE: __android_log_vprint(ANDROID_LOG_VERBOSE, "raylib", text, args); break;
  107. case LOG_DEBUG: __android_log_vprint(ANDROID_LOG_DEBUG, "raylib", text, args); break;
  108. case LOG_INFO: __android_log_vprint(ANDROID_LOG_INFO, "raylib", text, args); break;
  109. case LOG_WARNING: __android_log_vprint(ANDROID_LOG_WARN, "raylib", text, args); break;
  110. case LOG_ERROR: __android_log_vprint(ANDROID_LOG_ERROR, "raylib", text, args); break;
  111. case LOG_FATAL: __android_log_vprint(ANDROID_LOG_FATAL, "raylib", text, args); break;
  112. default: break;
  113. }
  114. #else
  115. char buffer[MAX_TRACELOG_MSG_LENGTH] = { 0 };
  116. switch (logType)
  117. {
  118. case LOG_TRACE: strcpy(buffer, "TRACE: "); break;
  119. case LOG_DEBUG: strcpy(buffer, "DEBUG: "); break;
  120. case LOG_INFO: strcpy(buffer, "INFO: "); break;
  121. case LOG_WARNING: strcpy(buffer, "WARNING: "); break;
  122. case LOG_ERROR: strcpy(buffer, "ERROR: "); break;
  123. case LOG_FATAL: strcpy(buffer, "FATAL: "); break;
  124. default: break;
  125. }
  126. unsigned int textSize = (unsigned int)strlen(text);
  127. memcpy(buffer + strlen(buffer), text, (textSize < (MAX_TRACELOG_MSG_LENGTH - 12))? textSize : (MAX_TRACELOG_MSG_LENGTH - 12));
  128. strcat(buffer, "\n");
  129. vprintf(buffer, args);
  130. fflush(stdout);
  131. #endif
  132. va_end(args);
  133. if (logType == LOG_FATAL) exit(EXIT_FAILURE); // If fatal logging, exit program
  134. #endif // SUPPORT_TRACELOG
  135. }
  136. // Internal memory allocator
  137. // NOTE: Initializes to zero by default
  138. void *MemAlloc(unsigned int size)
  139. {
  140. void *ptr = RL_CALLOC(size, 1);
  141. return ptr;
  142. }
  143. // Internal memory reallocator
  144. void *MemRealloc(void *ptr, unsigned int size)
  145. {
  146. void *ret = RL_REALLOC(ptr, size);
  147. return ret;
  148. }
  149. // Internal memory free
  150. void MemFree(void *ptr)
  151. {
  152. RL_FREE(ptr);
  153. }
  154. // Load data from file into a buffer
  155. unsigned char *LoadFileData(const char *fileName, int *dataSize)
  156. {
  157. unsigned char *data = NULL;
  158. *dataSize = 0;
  159. if (fileName != NULL)
  160. {
  161. if (loadFileData)
  162. {
  163. data = loadFileData(fileName, dataSize);
  164. return data;
  165. }
  166. #if defined(SUPPORT_STANDARD_FILEIO)
  167. FILE *file = fopen(fileName, "rb");
  168. if (file != NULL)
  169. {
  170. // WARNING: On binary streams SEEK_END could not be found,
  171. // using fseek() and ftell() could not work in some (rare) cases
  172. fseek(file, 0, SEEK_END);
  173. int size = ftell(file); // WARNING: ftell() returns 'long int', maximum size returned is INT_MAX (2147483647 bytes)
  174. fseek(file, 0, SEEK_SET);
  175. if (size > 0)
  176. {
  177. data = (unsigned char *)RL_CALLOC(size, sizeof(unsigned char));
  178. if (data != NULL)
  179. {
  180. // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements]
  181. size_t count = fread(data, sizeof(unsigned char), size, file);
  182. // WARNING: fread() returns a size_t value, usually 'unsigned int' (32bit compilation) and 'unsigned long long' (64bit compilation)
  183. // dataSize is unified along raylib as a 'int' type, so, for file-sizes > INT_MAX (2147483647 bytes) we have a limitation
  184. if (count > 2147483647)
  185. {
  186. TRACELOG(LOG_WARNING, "FILEIO: [%s] File is bigger than 2147483647 bytes, avoid using LoadFileData()", fileName);
  187. RL_FREE(data);
  188. data = NULL;
  189. }
  190. else
  191. {
  192. *dataSize = (int)count;
  193. if ((*dataSize) != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded (%i bytes out of %i)", fileName, dataSize, count);
  194. else TRACELOG(LOG_INFO, "FILEIO: [%s] File loaded successfully", fileName);
  195. }
  196. }
  197. else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to allocated memory for file reading", fileName);
  198. }
  199. else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to read file", fileName);
  200. fclose(file);
  201. }
  202. else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName);
  203. #else
  204. TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback");
  205. #endif
  206. }
  207. else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
  208. return data;
  209. }
  210. // Unload file data allocated by LoadFileData()
  211. void UnloadFileData(unsigned char *data)
  212. {
  213. RL_FREE(data);
  214. }
  215. // Save data to file from buffer
  216. bool SaveFileData(const char *fileName, void *data, int dataSize)
  217. {
  218. bool success = false;
  219. if (fileName != NULL)
  220. {
  221. if (saveFileData)
  222. {
  223. return saveFileData(fileName, data, dataSize);
  224. }
  225. #if defined(SUPPORT_STANDARD_FILEIO)
  226. FILE *file = fopen(fileName, "wb");
  227. if (file != NULL)
  228. {
  229. // WARNING: fwrite() returns a size_t value, usually 'unsigned int' (32bit compilation) and 'unsigned long long' (64bit compilation)
  230. // and expects a size_t input value but as dataSize is limited to INT_MAX (2147483647 bytes), there shouldn't be a problem
  231. int count = (int)fwrite(data, sizeof(unsigned char), dataSize, file);
  232. if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write file", fileName);
  233. else if (count != dataSize) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", fileName);
  234. else TRACELOG(LOG_INFO, "FILEIO: [%s] File saved successfully", fileName);
  235. int result = fclose(file);
  236. if (result == 0) success = true;
  237. }
  238. else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName);
  239. #else
  240. TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback");
  241. #endif
  242. }
  243. else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
  244. return success;
  245. }
  246. // Export data to code (.h), returns true on success
  247. bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileName)
  248. {
  249. bool success = false;
  250. #ifndef TEXT_BYTES_PER_LINE
  251. #define TEXT_BYTES_PER_LINE 20
  252. #endif
  253. // NOTE: Text data buffer size is estimated considering raw data size in bytes
  254. // and requiring 6 char bytes for every byte: "0x00, "
  255. char *txtData = (char *)RL_CALLOC(dataSize*6 + 2000, sizeof(char));
  256. int byteCount = 0;
  257. byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n");
  258. byteCount += sprintf(txtData + byteCount, "// //\n");
  259. byteCount += sprintf(txtData + byteCount, "// DataAsCode exporter v1.0 - Raw data exported as an array of bytes //\n");
  260. byteCount += sprintf(txtData + byteCount, "// //\n");
  261. byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n");
  262. byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n");
  263. byteCount += sprintf(txtData + byteCount, "// //\n");
  264. byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2022-2025 Ramon Santamaria (@raysan5) //\n");
  265. byteCount += sprintf(txtData + byteCount, "// //\n");
  266. byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
  267. // Get file name from path
  268. char varFileName[256] = { 0 };
  269. strcpy(varFileName, GetFileNameWithoutExt(fileName));
  270. for (int i = 0; varFileName[i] != '\0'; i++)
  271. {
  272. // Convert variable name to uppercase
  273. if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; }
  274. // Replace non valid character for C identifier with '_'
  275. else if (varFileName[i] == '.' || varFileName[i] == '-' || varFileName[i] == '?' || varFileName[i] == '!' || varFileName[i] == '+') { varFileName[i] = '_'; }
  276. }
  277. byteCount += sprintf(txtData + byteCount, "#define %s_DATA_SIZE %i\n\n", varFileName, dataSize);
  278. byteCount += sprintf(txtData + byteCount, "static unsigned char %s_DATA[%s_DATA_SIZE] = { ", varFileName, varFileName);
  279. 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]);
  280. byteCount += sprintf(txtData + byteCount, "0x%x };\n", data[dataSize - 1]);
  281. // NOTE: Text data size exported is determined by '\0' (NULL) character
  282. success = SaveFileText(fileName, txtData);
  283. RL_FREE(txtData);
  284. if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Data as code exported successfully", fileName);
  285. else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export data as code", fileName);
  286. return success;
  287. }
  288. // Load text data from file, returns a '\0' terminated string
  289. // NOTE: text chars array should be freed manually
  290. char *LoadFileText(const char *fileName)
  291. {
  292. char *text = NULL;
  293. if (fileName != NULL)
  294. {
  295. if (loadFileText)
  296. {
  297. text = loadFileText(fileName);
  298. return text;
  299. }
  300. #if defined(SUPPORT_STANDARD_FILEIO)
  301. FILE *file = fopen(fileName, "rt");
  302. if (file != NULL)
  303. {
  304. // WARNING: When reading a file as 'text' file,
  305. // text mode causes carriage return-linefeed translation...
  306. // ...but using fseek() should return correct byte-offset
  307. fseek(file, 0, SEEK_END);
  308. unsigned int size = (unsigned int)ftell(file);
  309. fseek(file, 0, SEEK_SET);
  310. if (size > 0)
  311. {
  312. text = (char *)RL_CALLOC(size + 1, sizeof(char));
  313. if (text != NULL)
  314. {
  315. unsigned int count = (unsigned int)fread(text, sizeof(char), size, file);
  316. // WARNING: \r\n is converted to \n on reading, so,
  317. // read bytes count gets reduced by the number of lines
  318. if (count < size) text = RL_REALLOC(text, count + 1);
  319. // Zero-terminate the string
  320. text[count] = '\0';
  321. TRACELOG(LOG_INFO, "FILEIO: [%s] Text file loaded successfully", fileName);
  322. }
  323. else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to allocated memory for file reading", fileName);
  324. }
  325. else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to read text file", fileName);
  326. fclose(file);
  327. }
  328. else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName);
  329. #else
  330. TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback");
  331. #endif
  332. }
  333. else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
  334. return text;
  335. }
  336. // Unload file text data allocated by LoadFileText()
  337. void UnloadFileText(char *text)
  338. {
  339. RL_FREE(text);
  340. }
  341. // Save text data to file (write), string must be '\0' terminated
  342. bool SaveFileText(const char *fileName, const char *text)
  343. {
  344. bool success = false;
  345. if (fileName != NULL)
  346. {
  347. if (saveFileText)
  348. {
  349. return saveFileText(fileName, text);
  350. }
  351. #if defined(SUPPORT_STANDARD_FILEIO)
  352. FILE *file = fopen(fileName, "wt");
  353. if (file != NULL)
  354. {
  355. int count = fprintf(file, "%s", text);
  356. if (count < 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write text file", fileName);
  357. else TRACELOG(LOG_INFO, "FILEIO: [%s] Text file saved successfully", fileName);
  358. int result = fclose(file);
  359. if (result == 0) success = true;
  360. }
  361. else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName);
  362. #else
  363. TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback");
  364. #endif
  365. }
  366. else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
  367. return success;
  368. }
  369. #if defined(PLATFORM_ANDROID)
  370. // Initialize asset manager from android app
  371. void InitAssetManager(AAssetManager *manager, const char *dataPath)
  372. {
  373. assetManager = manager;
  374. internalDataPath = dataPath;
  375. }
  376. // Replacement for fopen()
  377. // Ref: https://developer.android.com/ndk/reference/group/asset
  378. FILE *android_fopen(const char *fileName, const char *mode)
  379. {
  380. if (mode[0] == 'w')
  381. {
  382. // fopen() is mapped to android_fopen() that only grants read access to
  383. // assets directory through AAssetManager but we want to also be able to
  384. // write data when required using the standard stdio FILE access functions
  385. // Ref: https://stackoverflow.com/questions/11294487/android-writing-saving-files-from-native-code-only
  386. #undef fopen
  387. return fopen(TextFormat("%s/%s", internalDataPath, fileName), mode);
  388. #define fopen(name, mode) android_fopen(name, mode)
  389. }
  390. else
  391. {
  392. // NOTE: AAsset provides access to read-only asset
  393. AAsset *asset = AAssetManager_open(assetManager, fileName, AASSET_MODE_UNKNOWN);
  394. if (asset != NULL)
  395. {
  396. // Get 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. // Module Internal Functions Definition
  411. //----------------------------------------------------------------------------------
  412. #if defined(PLATFORM_ANDROID)
  413. static int android_read(void *cookie, char *data, int dataSize)
  414. {
  415. return AAsset_read((AAsset *)cookie, data, dataSize);
  416. }
  417. static int android_write(void *cookie, const char *data, int dataSize)
  418. {
  419. TRACELOG(LOG_WARNING, "ANDROID: Failed to provide write access to APK");
  420. return EACCES;
  421. }
  422. static fpos_t android_seek(void *cookie, fpos_t offset, int whence)
  423. {
  424. return AAsset_seek((AAsset *)cookie, offset, whence);
  425. }
  426. static int android_close(void *cookie)
  427. {
  428. AAsset_close((AAsset *)cookie);
  429. return 0;
  430. }
  431. #endif // PLATFORM_ANDROID