diff --git a/src/audio.c b/src/audio.c index f0362b2d..51023280 100644 --- a/src/audio.c +++ b/src/audio.c @@ -185,7 +185,8 @@ typedef enum { // Module specific Functions Declaration //---------------------------------------------------------------------------------- #if defined(SUPPORT_FILEFORMAT_WAV) -static Wave LoadWAV(const char *fileName); // Load WAV file +static Wave LoadWAV(const char *fileName); // Load WAV file +static int SaveWAV(Wave wave, const char *fileName); // Save wave data as WAV file #endif #if defined(SUPPORT_FILEFORMAT_OGG) static Wave LoadOGG(const char *fileName); // Load OGG file @@ -878,84 +879,59 @@ void ExportWave(Wave wave, const char *fileName) { bool success = false; - if (IsFileExtension(fileName, ".wav")) + if (IsFileExtension(fileName, ".wav")) success = SaveWAV(wave, fileName); + else if (IsFileExtension(fileName, ".raw")) { - // Basic WAV headers structs - typedef struct { - char chunkID[4]; - int chunkSize; - char format[4]; - } RiffHeader; - - typedef struct { - char subChunkID[4]; - int subChunkSize; - short audioFormat; - short numChannels; - int sampleRate; - int byteRate; - short blockAlign; - short bitsPerSample; - } WaveFormat; - - typedef struct { - char subChunkID[4]; - int subChunkSize; - } WaveData; - - RiffHeader riffHeader; - WaveFormat waveFormat; - WaveData waveData; - - // Fill structs with data - riffHeader.chunkID[0] = 'R'; - riffHeader.chunkID[1] = 'I'; - riffHeader.chunkID[2] = 'F'; - riffHeader.chunkID[3] = 'F'; - riffHeader.chunkSize = 44 - 4 + wave.sampleCount*wave.sampleSize/8; - riffHeader.format[0] = 'W'; - riffHeader.format[1] = 'A'; - riffHeader.format[2] = 'V'; - riffHeader.format[3] = 'E'; - - waveFormat.subChunkID[0] = 'f'; - waveFormat.subChunkID[1] = 'm'; - waveFormat.subChunkID[2] = 't'; - waveFormat.subChunkID[3] = ' '; - waveFormat.subChunkSize = 16; - waveFormat.audioFormat = 1; - waveFormat.numChannels = wave.channels; - waveFormat.sampleRate = wave.sampleRate; - waveFormat.byteRate = wave.sampleRate*wave.sampleSize/8; - waveFormat.blockAlign = wave.sampleSize/8; - waveFormat.bitsPerSample = wave.sampleSize; - - waveData.subChunkID[0] = 'd'; - waveData.subChunkID[1] = 'a'; - waveData.subChunkID[2] = 't'; - waveData.subChunkID[3] = 'a'; - waveData.subChunkSize = wave.sampleCount*wave.channels*wave.sampleSize/8; - - FILE *wavFile = fopen(fileName, "wb"); - - if (wavFile == NULL) return; - - fwrite(&riffHeader, 1, sizeof(RiffHeader), wavFile); - fwrite(&waveFormat, 1, sizeof(WaveFormat), wavFile); - fwrite(&waveData, 1, sizeof(WaveData), wavFile); - - fwrite(wave.data, 1, wave.sampleCount*wave.channels*wave.sampleSize/8, wavFile); - - fclose(wavFile); - - success = true; + // Export raw sample data (without header) + // NOTE: It's up to the user to track wave parameters + FILE *rawFile = fopen(fileName, "wb"); + success = fwrite(wave.data, wave.sampleCount*wave.channels*wave.sampleSize/8, 1, rawFile); + fclose(rawFile); } - else if (IsFileExtension(fileName, ".raw")) { } // TODO: Support additional file formats to export wave sample data - + if (success) TraceLog(LOG_INFO, "Wave exported successfully: %s", fileName); else TraceLog(LOG_WARNING, "Wave could not be exported."); } +// Export wave sample data to code (.h) +void ExportWaveAsCode(Wave wave, const char *fileName) +{ + #define BYTES_TEXT_PER_LINE 20 + + char varFileName[256] = { 0 }; + int dataSize = wave.sampleCount*wave.channels*wave.sampleSize/8; + + FILE *txtFile = fopen(fileName, "wt"); + + fprintf(txtFile, "\n//////////////////////////////////////////////////////////////////////////////////\n"); + fprintf(txtFile, "// //\n"); + fprintf(txtFile, "// WaveAsCode exporter v1.0 - Wave data exported as an array of bytes //\n"); + fprintf(txtFile, "// //\n"); + fprintf(txtFile, "// more info and bugs-report: github.com/raysan5/raylib //\n"); + fprintf(txtFile, "// feedback and support: ray[at]raylib.com //\n"); + fprintf(txtFile, "// //\n"); + fprintf(txtFile, "// Copyright (c) 2018 Ramon Santamaria (@raysan5) //\n"); + fprintf(txtFile, "// //\n"); + fprintf(txtFile, "//////////////////////////////////////////////////////////////////////////////////\n\n"); + + // Get file name from path and convert variable name to uppercase + strcpy(varFileName, GetFileNameWithoutExt(fileName)); + for (int i = 0; varFileName[i] != '\0'; i++) if (varFileName[i] >= 'a' && varFileName[i] <= 'z') { varFileName[i] = varFileName[i] - 32; } + + fprintf(txtFile, "// Wave data information\n"); + fprintf(txtFile, "#define %s_SAMPLE_COUNT %i\n", varFileName, wave.sampleCount); + fprintf(txtFile, "#define %s_SAMPLE_RATE %i\n", varFileName, wave.sampleRate); + fprintf(txtFile, "#define %s_SAMPLE_SIZE %i\n", varFileName, wave.sampleSize); + fprintf(txtFile, "#define %s_CHANNELS %i\n\n", varFileName, wave.channels); + + // Write byte data as hexadecimal text + fprintf(txtFile, "static unsigned char %s_DATA[%i] = { ", varFileName, dataSize); + for (int i = 0; i < dataSize - 1; i++) fprintf(txtFile, ((i%BYTES_TEXT_PER_LINE == 0) ? "0x%x,\n" : "0x%x, "), ((unsigned char *)wave.data)[i]); + fprintf(txtFile, "0x%x };\n", ((unsigned char *)wave.data)[dataSize - 1]); + + fclose(txtFile); +} + // Play a sound void PlaySound(Sound sound) { @@ -1739,6 +1715,86 @@ static Wave LoadWAV(const char *fileName) return wave; } + +// Save wave data as WAV file +static int SaveWAV(Wave wave, const char *fileName) +{ + int success = 0; + int dataSize = wave.sampleCount*wave.channels*wave.sampleSize/8; + + // Basic WAV headers structs + typedef struct { + char chunkID[4]; + int chunkSize; + char format[4]; + } RiffHeader; + + typedef struct { + char subChunkID[4]; + int subChunkSize; + short audioFormat; + short numChannels; + int sampleRate; + int byteRate; + short blockAlign; + short bitsPerSample; + } WaveFormat; + + typedef struct { + char subChunkID[4]; + int subChunkSize; + } WaveData; + + FILE *wavFile = fopen(fileName, "wb"); + + if (wavFile == NULL) TraceLog(LOG_WARNING, "[%s] WAV audio file could not be created", fileName); + else + { + RiffHeader riffHeader; + WaveFormat waveFormat; + WaveData waveData; + + // Fill structs with data + riffHeader.chunkID[0] = 'R'; + riffHeader.chunkID[1] = 'I'; + riffHeader.chunkID[2] = 'F'; + riffHeader.chunkID[3] = 'F'; + riffHeader.chunkSize = 44 - 4 + wave.sampleCount*wave.sampleSize/8; + riffHeader.format[0] = 'W'; + riffHeader.format[1] = 'A'; + riffHeader.format[2] = 'V'; + riffHeader.format[3] = 'E'; + + waveFormat.subChunkID[0] = 'f'; + waveFormat.subChunkID[1] = 'm'; + waveFormat.subChunkID[2] = 't'; + waveFormat.subChunkID[3] = ' '; + waveFormat.subChunkSize = 16; + waveFormat.audioFormat = 1; + waveFormat.numChannels = wave.channels; + waveFormat.sampleRate = wave.sampleRate; + waveFormat.byteRate = wave.sampleRate*wave.sampleSize/8; + waveFormat.blockAlign = wave.sampleSize/8; + waveFormat.bitsPerSample = wave.sampleSize; + + waveData.subChunkID[0] = 'd'; + waveData.subChunkID[1] = 'a'; + waveData.subChunkID[2] = 't'; + waveData.subChunkID[3] = 'a'; + waveData.subChunkSize = dataSize; + + success = fwrite(&riffHeader, sizeof(RiffHeader), 1, wavFile); + success = fwrite(&waveFormat, sizeof(WaveFormat), 1, wavFile); + success = fwrite(&waveData, sizeof(WaveData), 1, wavFile); + + success = fwrite(wave.data, dataSize, 1, wavFile); + + fclose(wavFile); + } + + // If all data has been written correctly to file, success = 1 + return success; +} #endif #if defined(SUPPORT_FILEFORMAT_OGG) diff --git a/src/core.c b/src/core.c index af601e1d..2038e274 100644 --- a/src/core.c +++ b/src/core.c @@ -1766,11 +1766,11 @@ void StorageSaveValue(int position, int value) int fileSize = ftell(storageFile); // Size in bytes fseek(storageFile, 0, SEEK_SET); - if (fileSize < (position*4)) TraceLog(LOG_WARNING, "Storage position could not be found"); + if (fileSize < (position*sizeof(int))) TraceLog(LOG_WARNING, "Storage position could not be found"); else { - fseek(storageFile, (position*4), SEEK_SET); - fwrite(&value, 1, 4, storageFile); + fseek(storageFile, (position*sizeof(int)), SEEK_SET); + fwrite(&value, 1, sizeof(int), storageFile); } fclose(storageFile); diff --git a/src/raylib.h b/src/raylib.h index be2383b1..c94b9f3c 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1001,6 +1001,7 @@ RLAPI Image LoadImageEx(Color *pixels, int width, int height); RLAPI Image LoadImagePro(void *data, int width, int height, int format); // Load image from raw data with parameters RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data RLAPI void ExportImage(Image image, const char *fileName); // Export image data to file +RLAPI void ExportImageAsCode(Image image, const char *fileName); // Export image as code file defining an array of bytes RLAPI Texture2D LoadTexture(const char *fileName); // Load texture from file into GPU memory (VRAM) RLAPI Texture2D LoadTextureFromImage(Image image); // Load texture from image data RLAPI RenderTexture2D LoadRenderTexture(int width, int height); // Load texture for rendering (framebuffer) @@ -1091,9 +1092,10 @@ RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float s RLAPI int GetGlyphIndex(Font font, int character); // Get index position for a unicode character on font // Text string edition functions -RLAPI const char *FormatText(const char *text, ...); // Formatting of text with variables to 'embed' -RLAPI const char *SubText(const char *text, int position, int length); // Get a piece of a text string -RLAPI char **SplitText(char *text, char delimiter, int *strCount); // Split text string into multiple strings (memory should be freed manually!) +RLAPI const char *FormatText(const char *text, ...); // Formatting of text with variables to 'embed' +RLAPI const char *SubText(const char *text, int position, int length); // Get a piece of a text string +RLAPI char **SplitText(char *text, char delimiter, int *strCount); // Split text string into multiple strings (memory should be freed manually!) +RLAPI bool IsEqualText(const char *text1, const char *text2); // Check if two text string are equal //------------------------------------------------------------------------------------ // Basic 3d Shapes Drawing Functions (Module: models) @@ -1238,6 +1240,7 @@ RLAPI void UpdateSound(Sound sound, const void *data, int samplesCount);// Updat RLAPI void UnloadWave(Wave wave); // Unload wave data RLAPI void UnloadSound(Sound sound); // Unload sound RLAPI void ExportWave(Wave wave, const char *fileName); // Export wave data to file +RLAPI void ExportWaveAsCode(Wave wave, const char *fileName); // Export wave sample data to code (.h) // Wave/Sound management functions RLAPI void PlaySound(Sound sound); // Play a sound diff --git a/src/text.c b/src/text.c index a010666e..5c3c14a8 100644 --- a/src/text.c +++ b/src/text.c @@ -190,10 +190,6 @@ extern void LoadDefaultFont(void) if (counter > 512) counter = 0; // Security check... } - //FILE *myimage = fopen("default_font.raw", "wb"); - //fwrite(image.pixels, 1, 128*128*4, myimage); - //fclose(myimage); - Image image = LoadImageEx(imagePixels, imWidth, imHeight); ImageFormat(&image, UNCOMPRESSED_GRAY_ALPHA); @@ -823,6 +819,16 @@ char **SplitText(char *text, char delimiter, int *strCount) return strings; } +// Check if two text string are equal +bool IsEqualText(const char *text1, const char *text2) +{ + bool result = false; + + if (strcmp(text1, text2) == 0) result = true; + + return result; +} + //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- diff --git a/src/textures.c b/src/textures.c index a0e74637..4d3917c0 100644 --- a/src/textures.c +++ b/src/textures.c @@ -60,7 +60,7 @@ #include "raylib.h" // Declares module functions #include // Required for: malloc(), free() -#include // Required for: strcmp(), strrchr(), strncmp() +#include // Required for: strlen() #include "rlgl.h" // raylib OpenGL abstraction layer to OpenGL 1.1, 3.3 or ES2 // Required for: rlLoadTexture() rlDeleteTextures(), @@ -147,8 +147,8 @@ static Image LoadDDS(const char *fileName); // Load DDS file static Image LoadPKM(const char *fileName); // Load PKM file #endif #if defined(SUPPORT_FILEFORMAT_KTX) -static Image LoadKTX(const char *fileName); // Load KTX file -static void SaveKTX(Image image, const char *fileName); // Save image data as KTX file +static Image LoadKTX(const char *fileName); // Load KTX file +static int SaveKTX(Image image, const char *fileName); // Save image data as KTX file #endif #if defined(SUPPORT_FILEFORMAT_PVR) static Image LoadPVR(const char *fileName); // Load PVR file @@ -728,23 +728,60 @@ void ExportImage(Image image, const char *fileName) else if (IsFileExtension(fileName, ".bmp")) success = stbi_write_bmp(fileName, image.width, image.height, 4, imgData); else if (IsFileExtension(fileName, ".tga")) success = stbi_write_tga(fileName, image.width, image.height, 4, imgData); else if (IsFileExtension(fileName, ".jpg")) success = stbi_write_jpg(fileName, image.width, image.height, 4, imgData, 80); // JPG quality: between 1 and 100 - else if (IsFileExtension(fileName, ".ktx")) SaveKTX(image, fileName); + else if (IsFileExtension(fileName, ".ktx")) success = SaveKTX(image, fileName); else if (IsFileExtension(fileName, ".raw")) { // Export raw pixel data (without header) // NOTE: It's up to the user to track image parameters FILE *rawFile = fopen(fileName, "wb"); - fwrite(image.data, GetPixelDataSize(image.width, image.height, image.format), 1, rawFile); + success = fwrite(image.data, GetPixelDataSize(image.width, image.height, image.format), 1, rawFile); fclose(rawFile); } - else if (IsFileExtension(fileName, ".h")) { } // TODO: Export pixel data as an array of bytes - + if (success != 0) TraceLog(LOG_INFO, "Image exported successfully: %s", fileName); else TraceLog(LOG_WARNING, "Image could not be exported."); free(imgData); } +// Export image as code file (.h) defining an array of bytes +void ExportImageAsCode(Image image, const char *fileName) +{ + #define BYTES_TEXT_PER_LINE 20 + + char varFileName[256] = { 0 }; + int dataSize = GetPixelDataSize(image.width, image.height, image.format); + + FILE *txtFile = fopen(fileName, "wt"); + + fprintf(txtFile, "\n//////////////////////////////////////////////////////////////////////////////////////\n"); + fprintf(txtFile, "// //\n"); + fprintf(txtFile, "// ImageAsCode exporter v1.0 - Image pixel data exported as an array of bytes //\n"); + fprintf(txtFile, "// //\n"); + fprintf(txtFile, "// more info and bugs-report: github.com/raysan5/raylib //\n"); + fprintf(txtFile, "// feedback and support: ray[at]raylib.com //\n"); + fprintf(txtFile, "// //\n"); + fprintf(txtFile, "// Copyright (c) 2018 Ramon Santamaria (@raysan5) //\n"); + fprintf(txtFile, "// //\n"); + fprintf(txtFile, "////////////////////////////////////////////////////////////////////////////////////////\n\n"); + + // Get file name from path and convert variable name to uppercase + strcpy(varFileName, GetFileNameWithoutExt(fileName)); + for (int i = 0; varFileName[i] != '\0'; i++) if (varFileName[i] >= 'a' && varFileName[i] <= 'z') { varFileName[i] = varFileName[i] - 32; } + + // Add image information + fprintf(txtFile, "// Image data information\n"); + fprintf(txtFile, "#define %s_WIDTH %i\n", varFileName, image.width); + fprintf(txtFile, "#define %s_HEIGHT %i\n", varFileName, image.height); + fprintf(txtFile, "#define %s_FORMAT %i // raylib internal pixel format\n\n", varFileName, image.format); + + fprintf(txtFile, "static unsigned char %s_DATA[%i] = { ", varFileName, dataSize); + for (int i = 0; i < dataSize - 1; i++) fprintf(txtFile, ((i%BYTES_TEXT_PER_LINE == 0) ? "0x%x,\n" : "0x%x, "), ((unsigned char *)image.data)[i]); + fprintf(txtFile, "0x%x };\n", ((unsigned char *)image.data)[dataSize - 1]); + + fclose(txtFile); +} + // Copy an image to a new image Image ImageCopy(Image image) { @@ -2669,11 +2706,11 @@ static Image LoadDDS(const char *fileName) else { // Verify the type of file - char filecode[4]; + char ddsHeaderId[4]; - fread(filecode, 4, 1, ddsFile); + fread(ddsHeaderId, 4, 1, ddsFile); - if (strncmp(filecode, "DDS ", 4) != 0) + if ((ddsHeaderId[0] != 'D') || (ddsHeaderId[1] != 'D') || (ddsHeaderId[2] != 'S') || (ddsHeaderId[3] != ' ')) { TraceLog(LOG_WARNING, "[%s] DDS file does not seem to be a valid image", fileName); } @@ -2853,7 +2890,7 @@ static Image LoadPKM(const char *fileName) // Get the image header fread(&pkmHeader, sizeof(PKMHeader), 1, pkmFile); - if (strncmp(pkmHeader.id, "PKM ", 4) != 0) + if ((pkmHeader.id[0] != 'P') || (pkmHeader.id[1] != 'K') || (pkmHeader.id[2] != 'M') || (ddsHeaderId[3] != ' ')) { TraceLog(LOG_WARNING, "[%s] PKM file does not seem to be a valid image", fileName); } @@ -2988,8 +3025,10 @@ static Image LoadKTX(const char *fileName) // Save image data as KTX file // NOTE: By default KTX 1.1 spec is used, 2.0 is still on draft (01Oct2018) -static void SaveKTX(Image image, const char *fileName) +static int SaveKTX(Image image, const char *fileName) { + int success = 0; + // KTX file Header (64 bytes) // v1.1 - https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/ // v2.0 - http://github.khronos.org/KTX-Specification/ - still on draft, not ready for implementation @@ -3050,7 +3089,7 @@ static void SaveKTX(Image image, const char *fileName) if (ktxHeader.glFormat == -1) TraceLog(LOG_WARNING, "Image format not supported for KTX export."); else { - fwrite(&ktxHeader, 1, sizeof(KTXHeader), ktxFile); + success = fwrite(&ktxHeader, sizeof(KTXHeader), 1, ktxFile); int width = image.width; int height = image.height; @@ -3060,8 +3099,8 @@ static void SaveKTX(Image image, const char *fileName) for (int i = 0; i < image.mipmaps; i++) { unsigned int dataSize = GetPixelDataSize(width, height, image.format); - fwrite(&dataSize, 1, sizeof(unsigned int), ktxFile); - fwrite((unsigned char *)image.data + dataOffset, 1, dataSize, ktxFile); + success = fwrite(&dataSize, sizeof(unsigned int), 1, ktxFile); + success = fwrite((unsigned char *)image.data + dataOffset, dataSize, 1, ktxFile); width /= 2; height /= 2; @@ -3071,6 +3110,9 @@ static void SaveKTX(Image image, const char *fileName) fclose(ktxFile); // Close file pointer } + + // If all data has been written correctly to file, success = 1 + return success; } #endif