#include "raylib.h"
#if !defined(EXTERNAL_CONFIG_FLAGS)
#include "config.h"
#endif
#include "utils.h"
#if defined(PLATFORM_ANDROID)
#include <errno.h>
#include <android/log.h>
#include <android/asset_manager.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#ifndef MAX_TRACELOG_MSG_LENGTH
#define MAX_TRACELOG_MSG_LENGTH 256
#endif
static int logTypeLevel = LOG_INFO;
static TraceLogCallback traceLog = NULL; static LoadFileDataCallback loadFileData = NULL; static SaveFileDataCallback saveFileData = NULL; static LoadFileTextCallback loadFileText = NULL; static SaveFileTextCallback saveFileText = NULL;
void SetTraceLogCallback(TraceLogCallback callback) { traceLog = callback; } void SetLoadFileDataCallback(LoadFileDataCallback callback) { loadFileData = callback; } void SetSaveFileDataCallback(SaveFileDataCallback callback) { saveFileData = callback; } void SetLoadFileTextCallback(LoadFileTextCallback callback) { loadFileText = callback; } void SetSaveFileTextCallback(SaveFileTextCallback callback) { saveFileText = callback; }
#if defined(PLATFORM_ANDROID)
static AAssetManager *assetManager = NULL; static const char *internalDataPath = NULL; #endif
#if defined(PLATFORM_ANDROID)
FILE *funopen(const void *cookie, int (*readfn)(void *, char *, int), int (*writefn)(void *, const char *, int),
fpos_t (*seekfn)(void *, fpos_t, int), int (*closefn)(void *));
static int android_read(void *cookie, char *buf, int size);
static int android_write(void *cookie, const char *buf, int size);
static fpos_t android_seek(void *cookie, fpos_t offset, int whence);
static int android_close(void *cookie);
#endif
void SetTraceLogLevel(int logType) { logTypeLevel = logType; }
void TraceLog(int logType, const char *text, ...)
{
#if defined(SUPPORT_TRACELOG)
if (logType < logTypeLevel) return;
va_list args;
va_start(args, text);
if (traceLog)
{
traceLog(logType, text, args);
va_end(args);
return;
}
#if defined(PLATFORM_ANDROID)
switch (logType)
{
case LOG_TRACE: __android_log_vprint(ANDROID_LOG_VERBOSE, "raylib", text, args); break;
case LOG_DEBUG: __android_log_vprint(ANDROID_LOG_DEBUG, "raylib", text, args); break;
case LOG_INFO: __android_log_vprint(ANDROID_LOG_INFO, "raylib", text, args); break;
case LOG_WARNING: __android_log_vprint(ANDROID_LOG_WARN, "raylib", text, args); break;
case LOG_ERROR: __android_log_vprint(ANDROID_LOG_ERROR, "raylib", text, args); break;
case LOG_FATAL: __android_log_vprint(ANDROID_LOG_FATAL, "raylib", text, args); break;
default: break;
}
#else
char buffer[MAX_TRACELOG_MSG_LENGTH] = { 0 };
switch (logType)
{
case LOG_TRACE: strcpy(buffer, "TRACE: "); break;
case LOG_DEBUG: strcpy(buffer, "DEBUG: "); break;
case LOG_INFO: strcpy(buffer, "INFO: "); break;
case LOG_WARNING: strcpy(buffer, "WARNING: "); break;
case LOG_ERROR: strcpy(buffer, "ERROR: "); break;
case LOG_FATAL: strcpy(buffer, "FATAL: "); break;
default: break;
}
unsigned int textSize = (unsigned int)strlen(text);
memcpy(buffer + strlen(buffer), text, (textSize < (MAX_TRACELOG_MSG_LENGTH - 12))? textSize : (MAX_TRACELOG_MSG_LENGTH - 12));
strcat(buffer, "\n");
vprintf(buffer, args);
fflush(stdout);
#endif
va_end(args);
if (logType == LOG_FATAL) exit(EXIT_FAILURE);
#endif }
void *MemAlloc(unsigned int size)
{
void *ptr = RL_CALLOC(size, 1);
return ptr;
}
void *MemRealloc(void *ptr, unsigned int size)
{
void *ret = RL_REALLOC(ptr, size);
return ret;
}
void MemFree(void *ptr)
{
RL_FREE(ptr);
}
unsigned char *LoadFileData(const char *fileName, int *dataSize)
{
unsigned char *data = NULL;
*dataSize = 0;
if (fileName != NULL)
{
if (loadFileData)
{
data = loadFileData(fileName, dataSize);
return data;
}
#if defined(SUPPORT_STANDARD_FILEIO)
FILE *file = fopen(fileName, "rb");
if (file != NULL)
{
fseek(file, 0, SEEK_END);
int size = ftell(file); fseek(file, 0, SEEK_SET);
if (size > 0)
{
data = (unsigned char *)RL_MALLOC(size*sizeof(unsigned char));
if (data != NULL)
{
size_t count = fread(data, sizeof(unsigned char), size, file);
if (count > 2147483647)
{
TRACELOG(LOG_WARNING, "FILEIO: [%s] File is bigger than 2147483647 bytes, avoid using LoadFileData()", fileName);
RL_FREE(data);
data = NULL;
}
else
{
*dataSize = (int)count;
if ((*dataSize) != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded (%i bytes out of %i)", fileName, dataSize, count);
else TRACELOG(LOG_INFO, "FILEIO: [%s] File loaded successfully", fileName);
}
}
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to allocated memory for file reading", fileName);
}
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to read file", fileName);
fclose(file);
}
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName);
#else
TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback");
#endif
}
else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
return data;
}
void UnloadFileData(unsigned char *data)
{
RL_FREE(data);
}
bool SaveFileData(const char *fileName, void *data, int dataSize)
{
bool success = false;
if (fileName != NULL)
{
if (saveFileData)
{
return saveFileData(fileName, data, dataSize);
}
#if defined(SUPPORT_STANDARD_FILEIO)
FILE *file = fopen(fileName, "wb");
if (file != NULL)
{
int count = (int)fwrite(data, sizeof(unsigned char), dataSize, file);
if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write file", fileName);
else if (count != dataSize) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", fileName);
else TRACELOG(LOG_INFO, "FILEIO: [%s] File saved successfully", fileName);
int result = fclose(file);
if (result == 0) success = true;
}
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open file", fileName);
#else
TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback");
#endif
}
else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
return success;
}
bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileName)
{
bool success = false;
#ifndef TEXT_BYTES_PER_LINE
#define TEXT_BYTES_PER_LINE 20
#endif
char *txtData = (char *)RL_CALLOC(dataSize*6 + 2000, sizeof(char));
int byteCount = 0;
byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "// DataAsCode exporter v1.0 - Raw data exported as an array of bytes //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "// more info and bugs-report: github.com/raysan5/raylib //\n");
byteCount += sprintf(txtData + byteCount, "// feedback and support: ray[at]raylib.com //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "// Copyright (c) 2022-2024 Ramon Santamaria (@raysan5) //\n");
byteCount += sprintf(txtData + byteCount, "// //\n");
byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n\n");
char varFileName[256] = { 0 };
strcpy(varFileName, GetFileNameWithoutExt(fileName));
for (int i = 0; varFileName[i] != '\0'; i++)
{
if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; }
else if (varFileName[i] == '.' || varFileName[i] == '-' || varFileName[i] == '?' || varFileName[i] == '!' || varFileName[i] == '+') { varFileName[i] = '_'; }
}
byteCount += sprintf(txtData + byteCount, "#define %s_DATA_SIZE %i\n\n", varFileName, dataSize);
byteCount += sprintf(txtData + byteCount, "static unsigned char %s_DATA[%s_DATA_SIZE] = { ", varFileName, varFileName);
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]);
byteCount += sprintf(txtData + byteCount, "0x%x };\n", data[dataSize - 1]);
success = SaveFileText(fileName, txtData);
RL_FREE(txtData);
if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Data as code exported successfully", fileName);
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export data as code", fileName);
return success;
}
char *LoadFileText(const char *fileName)
{
char *text = NULL;
if (fileName != NULL)
{
if (loadFileText)
{
text = loadFileText(fileName);
return text;
}
#if defined(SUPPORT_STANDARD_FILEIO)
FILE *file = fopen(fileName, "rt");
if (file != NULL)
{
fseek(file, 0, SEEK_END);
unsigned int size = (unsigned int)ftell(file);
fseek(file, 0, SEEK_SET);
if (size > 0)
{
text = (char *)RL_MALLOC((size + 1)*sizeof(char));
if (text != NULL)
{
unsigned int count = (unsigned int)fread(text, sizeof(char), size, file);
if (count < size) text = RL_REALLOC(text, count + 1);
text[count] = '\0';
TRACELOG(LOG_INFO, "FILEIO: [%s] Text file loaded successfully", fileName);
}
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to allocated memory for file reading", fileName);
}
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to read text file", fileName);
fclose(file);
}
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName);
#else
TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback");
#endif
}
else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
return text;
}
void UnloadFileText(char *text)
{
RL_FREE(text);
}
bool SaveFileText(const char *fileName, char *text)
{
bool success = false;
if (fileName != NULL)
{
if (saveFileText)
{
return saveFileText(fileName, text);
}
#if defined(SUPPORT_STANDARD_FILEIO)
FILE *file = fopen(fileName, "wt");
if (file != NULL)
{
int count = fprintf(file, "%s", text);
if (count < 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write text file", fileName);
else TRACELOG(LOG_INFO, "FILEIO: [%s] Text file saved successfully", fileName);
int result = fclose(file);
if (result == 0) success = true;
}
else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to open text file", fileName);
#else
TRACELOG(LOG_WARNING, "FILEIO: Standard file io not supported, use custom file callback");
#endif
}
else TRACELOG(LOG_WARNING, "FILEIO: File name provided is not valid");
return success;
}
#if defined(PLATFORM_ANDROID)
void InitAssetManager(AAssetManager *manager, const char *dataPath)
{
assetManager = manager;
internalDataPath = dataPath;
}
FILE *android_fopen(const char *fileName, const char *mode)
{
if (mode[0] == 'w')
{
#undef fopen
return fopen(TextFormat("%s/%s", internalDataPath, fileName), mode);
#define fopen(name, mode) android_fopen(name, mode)
}
else
{
AAsset *asset = AAssetManager_open(assetManager, fileName, AASSET_MODE_UNKNOWN);
if (asset != NULL)
{
return funopen(asset, android_read, android_write, android_seek, android_close);
}
else
{
#undef fopen
return fopen(TextFormat("%s/%s", internalDataPath, fileName), mode);
#define fopen(name, mode) android_fopen(name, mode)
}
}
}
#endif
#if defined(PLATFORM_ANDROID)
static int android_read(void *cookie, char *data, int dataSize)
{
return AAsset_read((AAsset *)cookie, data, dataSize);
}
static int android_write(void *cookie, const char *data, int dataSize)
{
TRACELOG(LOG_WARNING, "ANDROID: Failed to provide write access to APK");
return EACCES;
}
static fpos_t android_seek(void *cookie, fpos_t offset, int whence)
{
return AAsset_seek((AAsset *)cookie, offset, whence);
}
static int android_close(void *cookie)
{
AAsset_close((AAsset *)cookie);
return 0;
}
#endif