aboutsummaryrefslogtreecommitdiff
path: root/raylib/src/utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'raylib/src/utils.c')
-rw-r--r--raylib/src/utils.c510
1 files changed, 510 insertions, 0 deletions
diff --git a/raylib/src/utils.c b/raylib/src/utils.c
new file mode 100644
index 0000000..c5d9748
--- /dev/null
+++ b/raylib/src/utils.c
@@ -0,0 +1,510 @@
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//----------------------------------------------------------------------------------
62static int logTypeLevel = LOG_INFO; // Minimum log type level
63
64static TraceLogCallback traceLog = NULL; // TraceLog callback function pointer
65static LoadFileDataCallback loadFileData = NULL; // LoadFileData callback function pointer
66static SaveFileDataCallback saveFileData = NULL; // SaveFileText callback function pointer
67static LoadFileTextCallback loadFileText = NULL; // LoadFileText callback function pointer
68static SaveFileTextCallback saveFileText = NULL; // SaveFileText callback function pointer
69
70//----------------------------------------------------------------------------------
71// Functions to set internal callbacks
72//----------------------------------------------------------------------------------
73void SetTraceLogCallback(TraceLogCallback callback) { traceLog = callback; } // Set custom trace log
74void SetLoadFileDataCallback(LoadFileDataCallback callback) { loadFileData = callback; } // Set custom file data loader
75void SetSaveFileDataCallback(SaveFileDataCallback callback) { saveFileData = callback; } // Set custom file data saver
76void SetLoadFileTextCallback(LoadFileTextCallback callback) { loadFileText = callback; } // Set custom file text loader
77void SetSaveFileTextCallback(SaveFileTextCallback callback) { saveFileText = callback; } // Set custom file text saver
78
79#if defined(PLATFORM_ANDROID)
80static AAssetManager *assetManager = NULL; // Android assets manager pointer
81static const char *internalDataPath = NULL; // Android internal data path
82#endif
83
84//----------------------------------------------------------------------------------
85// Module specific Functions Declaration
86//----------------------------------------------------------------------------------
87#if defined(PLATFORM_ANDROID)
88FILE *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
91static int android_read(void *cookie, char *buf, int size);
92static int android_write(void *cookie, const char *buf, int size);
93static fpos_t android_seek(void *cookie, fpos_t offset, int whence);
94static int android_close(void *cookie);
95#endif
96
97//----------------------------------------------------------------------------------
98// Module Functions Definition - Utilities
99//----------------------------------------------------------------------------------
100
101// Set the current threshold (minimum) log level
102void SetTraceLogLevel(int logType) { logTypeLevel = logType; }
103
104// Show trace log messages (LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_DEBUG)
105void 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
162void *MemAlloc(unsigned int size)
163{
164 void *ptr = RL_CALLOC(size, 1);
165 return ptr;
166}
167
168// Internal memory reallocator
169void *MemRealloc(void *ptr, unsigned int size)
170{
171 void *ret = RL_REALLOC(ptr, size);
172 return ret;
173}
174
175// Internal memory free
176void MemFree(void *ptr)
177{
178 RL_FREE(ptr);
179}
180
181// Load data from file into a buffer
182unsigned 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()
248void UnloadFileData(unsigned char *data)
249{
250 RL_FREE(data);
251}
252
253// Save data to file from buffer
254bool 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
291bool 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
345char *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()
402void UnloadFileText(char *text)
403{
404 RL_FREE(text);
405}
406
407// Save text data to file (write), string must be '\0' terminated
408bool 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
443void 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
451FILE *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)
488static int android_read(void *cookie, char *data, int dataSize)
489{
490 return AAsset_read((AAsset *)cookie, data, dataSize);
491}
492
493static 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
500static fpos_t android_seek(void *cookie, fpos_t offset, int whence)
501{
502 return AAsset_seek((AAsset *)cookie, offset, whence);
503}
504
505static int android_close(void *cookie)
506{
507 AAsset_close((AAsset *)cookie);
508 return 0;
509}
510#endif // PLATFORM_ANDROID

Generated with cgit - Back to sebastiano.tronto.net