From 202f45415c98df2201202ba8edb10b6496cbeb62 Mon Sep 17 00:00:00 2001 From: raysan5 Date: Tue, 27 Dec 2016 17:42:22 +0100 Subject: [PATCH] rRES raylib resources custom file format support First version of custom raylib resources file format -IN DEVELOPMENT- --- src/raylib.h | 20 +++ src/rres.h | 444 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 464 insertions(+) create mode 100644 src/rres.h diff --git a/src/raylib.h b/src/raylib.h index 6fd960dc..e2e4ee13 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -602,6 +602,26 @@ typedef enum { HMD_FOVE_VR, } VrDevice; +// rRES data returned when reading a resource, it contains all required data for user (24 byte) +typedef struct { + unsigned int type; // Resource type (4 byte) + + unsigned int param1; // Resouce parameter 1 (4 byte) + unsigned int param2; // Resouce parameter 2 (4 byte) + unsigned int param3; // Resouce parameter 3 (4 byte) + unsigned int param4; // Resouce parameter 4 (4 byte) + + void *data; // Resource data pointer (4 byte) +} RRESData; + +typedef enum { + RRES_RAW = 0, + RRES_IMAGE, + RRES_WAVE, + RRES_VERTEX, + RRES_TEXT +} RRESDataType; + #ifdef __cplusplus extern "C" { // Prevents name mangling of functions #endif diff --git a/src/rres.h b/src/rres.h new file mode 100644 index 00000000..dcf7be3f --- /dev/null +++ b/src/rres.h @@ -0,0 +1,444 @@ +/********************************************************************************************** +* +* rres - raylib Resource custom format management functions +* +* Basic functions to load/save rRES resource files +* +* External libs: +* tinfl - DEFLATE decompression functions +* +* Module Configuration Flags: +* +* #define RREM_IMPLEMENTATION +* Generates the implementation of the library into the included file. +* +* +* Copyright (c) 2016-2017 Ramon Santamaria (@raysan5) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#ifndef RRES_H +#define RRES_H + +#if !defined(RRES_STANDALONE) + #include "raylib.h" +#endif + +//#define RRES_STATIC +#ifdef RRES_STATIC + #define RRESDEF static // Functions just visible to module including this file +#else + #ifdef __cplusplus + #define RRESDEF extern "C" // Functions visible from other files (no name mangling of functions in C++) + #else + #define RRESDEF extern // Functions visible from other files + #endif +#endif + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#define MAX_RESOURCES_SUPPORTED 256 + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +// NOTE: Some types are required for RAYGUI_STANDALONE usage +//---------------------------------------------------------------------------------- +#if defined(RRES_STANDALONE) + // rRES data returned when reading a resource, it contains all required data for user (24 byte) + typedef struct { + unsigned int type; // Resource type (4 byte) + + unsigned int param1; // Resouce parameter 1 (4 byte) + unsigned int param2; // Resouce parameter 2 (4 byte) + unsigned int param3; // Resouce parameter 3 (4 byte) + unsigned int param4; // Resouce parameter 4 (4 byte) + + void *data; // Resource data pointer (4 byte) + } RRESData; + + typedef enum { + RRES_RAW = 0, + RRES_IMAGE, + RRES_WAVE, + RRES_VERTEX, + RRES_TEXT + } RRESDataType; +#endif + +//---------------------------------------------------------------------------------- +// Global variables +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +RRESDEF RRESData LoadResource(const char *rresFileName); +RRESDEF RRESData LoadResourceById(const char *rresFileName, int rresId); +RRESDEF void UnloadResource(RRESData rres); + +#endif // RRES_H + + +/*********************************************************************************** +* +* RRES IMPLEMENTATION +* +************************************************************************************/ + +#if defined(RRES_IMPLEMENTATION) + +#include // Required for: FILE, fopen(), fclose() + +// Check if custom malloc/free functions defined, if not, using standard ones +#if !defined(RRES_MALLOC) + #include // Required for: malloc(), free() + + #define RRES_MALLOC(size) malloc(size) + #define RRES_FREE(ptr) free(ptr) +#endif + +#include "external/tinfl.c" // Required for: tinfl_decompress_mem_to_mem() + // NOTE: DEFLATE algorythm data decompression + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- + +// rRES file header (8 byte) +typedef struct { + char id[4]; // File identifier: rRES (4 byte) + unsigned short version; // File version and subversion (2 byte) + unsigned short count; // Number of resources in this file (2 byte) +} RRESFileHeader; + +// rRES info header, every resource includes this header (12 byte + 16 byte) +typedef struct { + unsigned short id; // Resource unique identifier (2 byte) + unsigned char dataType; // Resource data type (1 byte) + unsigned char compType; // Resource data compression type (1 byte) + unsigned int dataSize; // Resource data size (compressed or not, only DATA) (4 byte) + unsigned int uncompSize; // Resource data size (uncompressed, only DATA) (4 byte) + + unsigned int param1; // Resouce parameter 1 (4 byte) + unsigned int param2; // Resouce parameter 2 (4 byte) + unsigned int param3; // Resouce parameter 3 (4 byte) + unsigned int param4; // Resouce parameter 4 (4 byte) +} RRESInfoHeader; + +// Compression types +typedef enum { + RRES_COMP_NONE = 0, // No data compression + RRES_COMP_DEFLATE, // DEFLATE compression + RRES_COMP_LZ4, // LZ4 compression + RRES_COMP_LZMA, // LZMA compression + // brotli, zopfli, gzip // Other compression algorythms... +} RRESCompressionType; + +#if defined(RRES_STANDALONE) +typedef enum { INFO = 0, ERROR, WARNING, DEBUG, OTHER } TraceLogType; +#endif + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Module specific Functions Declaration +//---------------------------------------------------------------------------------- +static void *DecompressData(const unsigned char *data, unsigned long compSize, int uncompSize); + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// Load resource from file (only one) +// NOTE: Returns uncompressed data with parameters, only first resource found +RRESDEF RRESData LoadResource(const char *fileName) +{ + RRESData rres = { 0 }; + + RRESFileHeader fileHeader; + RRESInfoHeader infoHeader; + + FILE *rresFile = fopen(fileName, "rb"); + + if (rresFile == NULL) TraceLog(WARNING, "[%s] rRES raylib resource file could not be opened", fileName); + else + { + // Read rres file info header + fread(&fileHeader.id[0], sizeof(char), 1, rresFile); + fread(&fileHeader.id[1], sizeof(char), 1, rresFile); + fread(&fileHeader.id[2], sizeof(char), 1, rresFile); + fread(&fileHeader.id[3], sizeof(char), 1, rresFile); + fread(&fileHeader.version, sizeof(short), 1, rresFile); + fread(&fileHeader.count, sizeof(short), 1, rresFile); + + // Verify "rRES" identifier + if ((fileHeader.id[0] != 'r') && (fileHeader.id[1] != 'R') && (fileHeader.id[2] != 'E') && (fileHeader.id[3] != 'S')) + { + TraceLog(WARNING, "[%s] This is not a valid raylib resource file", fileName); + } + else + { + // Read first resource info and parameters + fread(&infoHeader, sizeof(RRESInfoHeader), 1, rresFile); + + // Register data type and parameters + rres.type = infoHeader.dataType; + rres.param1 = infoHeader.param1; + rres.param2 = infoHeader.param2; + rres.param3 = infoHeader.param3; + rres.param4 = infoHeader.param4; + + // Read resource data block + void *data = RRES_MALLOC(infoHeader.dataSize); + fread(data, infoHeader.dataSize, 1, rresFile); + + if (infoHeader.compType == RRES_COMP_DEFLATE) + { + void *uncompData = DecompressData(data, infoHeader.dataSize, infoHeader.uncompSize); + + rres.data = uncompData; + + RRES_FREE(data); + } + else rres.data = data; + + if (rres.data != NULL) TraceLog(INFO, "[%s] Resource data loaded successfully", fileName); + } + + fclose(rresFile); + } + + return rres; +} + +// Load resource from file by id +// NOTE: Returns uncompressed data with parameters, search resource by id +RRESDEF RRESData LoadResourceById(const char *fileName, int rresId) +{ + RRESData rres = { 0 }; + + RRESFileHeader fileHeader; + RRESInfoHeader infoHeader; + + FILE *rresFile = fopen(fileName, "rb"); + + if (rresFile == NULL) TraceLog(WARNING, "[%s] rRES raylib resource file could not be opened", fileName); + else + { + // Read rres file info header + fread(&fileHeader.id[0], sizeof(char), 1, rresFile); + fread(&fileHeader.id[1], sizeof(char), 1, rresFile); + fread(&fileHeader.id[2], sizeof(char), 1, rresFile); + fread(&fileHeader.id[3], sizeof(char), 1, rresFile); + fread(&fileHeader.version, sizeof(short), 1, rresFile); + fread(&fileHeader.count, sizeof(short), 1, rresFile); + + // Verify "rRES" identifier + if ((fileHeader.id[0] != 'r') && (fileHeader.id[1] != 'R') && (fileHeader.id[2] != 'E') && (fileHeader.id[3] != 'S')) + { + TraceLog(WARNING, "[%s] This is not a valid raylib resource file", fileName); + } + else + { + for (int i = 0; i < fileHeader.count; i++) + { + // Read resource info and parameters + fread(&infoHeader, sizeof(RRESInfoHeader), 1, rresFile); + + if (infoHeader.id == rresId) + { + // Register data type and parameters + rres.type = infoHeader.dataType; + rres.param1 = infoHeader.param1; + rres.param2 = infoHeader.param2; + rres.param3 = infoHeader.param3; + rres.param4 = infoHeader.param4; + + // Read resource data block + void *data = RRES_MALLOC(infoHeader.dataSize); + fread(data, infoHeader.dataSize, 1, rresFile); + + if (infoHeader.compType == RRES_COMP_DEFLATE) + { + void *uncompData = DecompressData(data, infoHeader.dataSize, infoHeader.uncompSize); + + rres.data = uncompData; + + RRES_FREE(data); + } + else rres.data = data; + + if (rres.data != NULL) TraceLog(INFO, "[%s][ID %i] Resource data loaded successfully", fileName, (int)rresId); + } + else + { + // Skip required data to read next resource infoHeader + fseek(rresFile, infoHeader.dataSize, SEEK_CUR); + } + } + + if (rres.data == NULL) TraceLog(WARNING, "[%s][ID %i] Requested resource could not be found, wrong id?", fileName, (int)rresId); + } + + fclose(rresFile); + } + + return rres; +} + +RRESDEF void UnloadResource(RRESData rres) +{ + if (rres.data != NULL) free(rres.data); +} + +//---------------------------------------------------------------------------------- +// Module specific Functions Definition +//---------------------------------------------------------------------------------- + +// Data decompression function +// NOTE: Allocated data MUST be freed by user +static void *DecompressData(const unsigned char *data, unsigned long compSize, int uncompSize) +{ + int tempUncompSize; + void *uncompData; + + // Allocate buffer to hold decompressed data + uncompData = (mz_uint8 *)RRES_MALLOC((size_t)uncompSize); + + // Check correct memory allocation + if (uncompData == NULL) + { + TraceLog(WARNING, "Out of memory while decompressing data"); + } + else + { + // Decompress data + tempUncompSize = tinfl_decompress_mem_to_mem(uncompData, (size_t)uncompSize, data, compSize, 1); + + if (tempUncompSize == -1) + { + TraceLog(WARNING, "Data decompression failed"); + RRES_FREE(uncompData); + } + + if (uncompSize != (int)tempUncompSize) + { + TraceLog(WARNING, "Expected uncompressed size do not match, data may be corrupted"); + TraceLog(WARNING, " -- Expected uncompressed size: %i", uncompSize); + TraceLog(WARNING, " -- Returned uncompressed size: %i", tempUncompSize); + } + + TraceLog(INFO, "Data decompressed successfully from %u bytes to %u bytes", (mz_uint32)compSize, (mz_uint32)tempUncompSize); + } + + return uncompData; +} + + +// Some required functions for rres standalone module version +#if defined(RRES_STANDALONE) +// Outputs a trace log message (INFO, ERROR, WARNING) +// NOTE: If a file has been init, output log is written there +void TraceLog(int msgType, const char *text, ...) +{ + va_list args; + int traceDebugMsgs = 0; + +#ifdef DO_NOT_TRACE_DEBUG_MSGS + traceDebugMsgs = 0; +#endif + + switch (msgType) + { + case INFO: fprintf(stdout, "INFO: "); break; + case ERROR: fprintf(stdout, "ERROR: "); break; + case WARNING: fprintf(stdout, "WARNING: "); break; + case DEBUG: if (traceDebugMsgs) fprintf(stdout, "DEBUG: "); break; + default: break; + } + + if ((msgType != DEBUG) || ((msgType == DEBUG) && (traceDebugMsgs))) + { + va_start(args, text); + vfprintf(stdout, text, args); + va_end(args); + + fprintf(stdout, "\n"); + } + + if (msgType == ERROR) exit(1); // If ERROR message, exit program +} +#endif + +#endif // RAYGUI_IMPLEMENTATION + + +/* +//T LoadResource(const char *rresFileName, int resId); + +// ASSUMPTION: rRES files only contain one resource (solution to id requirement...) + +// Now, rres file check and data loading can be managed inside each function: +Image LoadImage(); // -> Texture2D +Wave LoadWave() // -> Sound, Music +const char *LoadText(); // -> Shader, Material + +// NOTE: RRESData uses void* data pointer, so we can load to image.data, wave.data, mesh.*, (unsigned char *) + +Image LoadImagePro(void *data, int width, int height, int format); +Image LoadImagePro(rres.data, rres.param1, rres.param2, rres.param3); + +Mesh LoadMeshEx(int numVertex, float *vData, float *vtData, float *vnData, Color *cData); +Mesh LoadMeshEx(rres.param1, rres.data, rres.data + offset, rres.data + offset*2, rres.data + offset*3); + +Shader LoadShaderV(const char *vsText, int vsLength); +Shader LoadShaderV(rres.data, rres.param1); + +Wave LoadWaveEx(void *data, int sampleCount, int sampleRate, int sampleSize, int channels); +Wave LoadWaveEx(rres.data, rres.param1, (int)rres.param2, (int)rres.param3, (int)rres.param4); + +// Max value for an unsigned short: 65535 + +// Parameters information depending on resource type (IMAGE, WAVE, MESH, TEXT) + +// Image data params +int imgWidth, imgHeight; +char colorFormat, mipmaps; + +// Wave data params +short sampleRate, bps; +char channels, reserved; + +// Mesh data params +int vertexCount, reserved; +short vertexTypesMask, vertexFormatsMask; + +// Text data params +int numChars; +char textFormat, language, charsetCode; +*/ \ No newline at end of file