diff --git a/demos/modules/gdisp/images/gfxconf.h b/demos/modules/gdisp/images/gfxconf.h index 6cd78b37..4ef9ab9e 100644 --- a/demos/modules/gdisp/images/gfxconf.h +++ b/demos/modules/gdisp/images/gfxconf.h @@ -51,5 +51,9 @@ #define GDISP_NEED_IMAGE_JPG FALSE #define GDISP_NEED_IMAGE_PNG FALSE +#define GFX_USE_GFILE TRUE +#define GFILE_NEED_ROMFS TRUE +//#define GFILE_NEED_NATIVEFS TRUE + #endif /* _GFXCONF_H */ diff --git a/demos/modules/gdisp/images/main.c b/demos/modules/gdisp/images/main.c index b87f663c..6a2e4418 100644 --- a/demos/modules/gdisp/images/main.c +++ b/demos/modules/gdisp/images/main.c @@ -29,15 +29,12 @@ #include "gfx.h" -#ifdef WIN32 - #define USE_MEMORY_FILE TRUE // Can be true or false for Win32 -#else - #define USE_MEMORY_FILE TRUE // Non-Win32 - use the compiled in image -#endif - -#if USE_MEMORY_FILE - #include "test-pal8.h" -#endif +/** + * The image file must be stored on a GFILE file-system. + * Use either GFILE_NEED_NATIVEFS or GFILE_NEED_ROMFS (or both). + * + * The ROMFS uses the file "romfs_files.h" to describe the set of files in the ROMFS. + */ static gdispImage myImage; @@ -52,13 +49,7 @@ int main(void) { sheight = gdispGetHeight(); // Set up IO for our image -#if USE_MEMORY_FILE - gdispImageSetMemoryReader(&myImage, test_pal8); -#else - gdispImageSetSimulFileReader(&myImage, "test-pal8.bmp"); -#endif - - gdispImageOpen(&myImage); + gdispImageOpenFile(&myImage, "test-pal8.bmp"); gdispImageDraw(&myImage, 0, 0, swidth, sheight, 0, 0); gdispImageClose(&myImage); diff --git a/demos/modules/gdisp/images/romfs_files.h b/demos/modules/gdisp/images/romfs_files.h new file mode 100644 index 00000000..a31dc7bf --- /dev/null +++ b/demos/modules/gdisp/images/romfs_files.h @@ -0,0 +1,7 @@ +/** + * This file contains the list of files for the ROMFS. + * + * The files have been converted using... + * file2c -dbcs infile outfile + */ +#include "romfs_testpal8.h" diff --git a/demos/modules/gdisp/images/test-pal8.h b/demos/modules/gdisp/images/romfs_testpal8.h similarity index 99% rename from demos/modules/gdisp/images/test-pal8.h rename to demos/modules/gdisp/images/romfs_testpal8.h index 9d1d7e0f..e6a353a2 100644 --- a/demos/modules/gdisp/images/test-pal8.h +++ b/demos/modules/gdisp/images/romfs_testpal8.h @@ -1,10 +1,10 @@ /** * This file was generated from "test-pal8.bmp" using... * - * file2c -cs test-pal8.bmp test-pal8.h + * file2c -dcs test-pal8.bmp romfs_testpal8.h * */ -static const unsigned char test_pal8[] = { +static const char test_pal8[] = { 0x42, 0x4D, 0x26, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x04, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x13, 0x0B, 0x00, 0x00, 0x13, 0x0B, 0x00, 0x00, 0xFC, 0x00, @@ -585,3 +585,9 @@ static const unsigned char test_pal8[] = { 0xC3, 0x93, 0xC4, 0x93, 0xC3, 0x93, 0xC4, 0x93, 0xC3, 0x93, 0xC4, 0x93, 0xC3, 0x93, 0xC4, 0x93, 0xC3, 0x93, 0xC4, 0x93, 0xC3, 0x00, }; + +#ifdef ROMFS_DIRENTRY_HEAD + static const ROMFS_DIRENTRY test_pal8_dir = { 0, 0, ROMFS_DIRENTRY_HEAD, "test-pal8.bmp", 9254, test_pal8 }; + #undef ROMFS_DIRENTRY_HEAD + #define ROMFS_DIRENTRY_HEAD &test_pal8_dir +#endif diff --git a/demos/modules/gdisp/images_animated/gfxconf.h b/demos/modules/gdisp/images_animated/gfxconf.h index cc38ed36..fddda437 100644 --- a/demos/modules/gdisp/images_animated/gfxconf.h +++ b/demos/modules/gdisp/images_animated/gfxconf.h @@ -51,5 +51,8 @@ #define GDISP_NEED_IMAGE_JPG FALSE #define GDISP_NEED_IMAGE_PNG FALSE +#define GFX_USE_GFILE TRUE +#define GFILE_NEED_ROMFS TRUE + #endif /* _GFXCONF_H */ diff --git a/demos/modules/gdisp/images_animated/main.c b/demos/modules/gdisp/images_animated/main.c index 9b2b5d2c..039cf584 100644 --- a/demos/modules/gdisp/images_animated/main.c +++ b/demos/modules/gdisp/images_animated/main.c @@ -29,23 +29,20 @@ #include "gfx.h" +/** + * The image file must be stored on a GFILE file-system. + * Use either GFILE_NEED_NATIVEFS or GFILE_NEED_ROMFS (or both). + * + * The ROMFS uses the file "romfs_files.h" to describe the set of files in the ROMFS. + */ + #define USE_IMAGE_CACHE FALSE // Only if you want to get performance at the expense of RAM #define MY_BG_COLOR RGB2COLOR(220, 220, 255) // Pale blue so we can see the transparent parts -#ifdef WIN32 - #define USE_MEMORY_FILE TRUE // Can be true or false for Win32 -#else - #define USE_MEMORY_FILE TRUE // Non-Win32 - use the compiled in image -#endif +static gdispImage myImage; #define SHOW_ERROR(color) gdispFillArea(errx, erry, errcx, errcy, color) -#if USE_MEMORY_FILE - #include "testanim.h" -#endif - -static gdispImage myImage; - /** * This demo display the animated gif (either directly from a file or from a * file encoded in flash. @@ -75,13 +72,8 @@ int main(void) { errcy = sheight; // Set up IO for our image -#if USE_MEMORY_FILE - gdispImageSetMemoryReader(&myImage, testanim); -#else - gdispImageSetFileReader(&myImage, "testanim.gif"); -#endif + if (!(gdispImageOpenFile(&myImage, "testanim.gif") & GDISP_IMAGE_ERR_UNRECOVERABLE)) { - if (gdispImageOpen(&myImage) == GDISP_IMAGE_ERR_OK) { gdispImageSetBgColor(&myImage, MY_BG_COLOR); // Adjust the error indicator area if necessary if (myImage.width > errx && myImage.height < sheight) { diff --git a/demos/modules/gdisp/images_animated/romfs_files.h b/demos/modules/gdisp/images_animated/romfs_files.h new file mode 100644 index 00000000..9cd40491 --- /dev/null +++ b/demos/modules/gdisp/images_animated/romfs_files.h @@ -0,0 +1,7 @@ +/** + * This file contains the list of files for the ROMFS. + * + * The files have been converted using... + * file2c -dbcs infile outfile + */ +#include "romfs_testanim.h" diff --git a/demos/modules/gdisp/images_animated/testanim.h b/demos/modules/gdisp/images_animated/romfs_testanim.h similarity index 99% rename from demos/modules/gdisp/images_animated/testanim.h rename to demos/modules/gdisp/images_animated/romfs_testanim.h index ce2ba8c7..cbda0d79 100644 --- a/demos/modules/gdisp/images_animated/testanim.h +++ b/demos/modules/gdisp/images_animated/romfs_testanim.h @@ -1,10 +1,10 @@ /** * This file was generated from "testanim.gif" using... * - * file2c -cs testanim.gif testanim.h + * file2c -dcs testanim.gif romfs_testanim.h * */ -static const unsigned char testanim[] = { +static const char testanim[] = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x99, 0x00, 0x73, 0x00, 0xE6, 0x46, 0x00, 0x04, 0x07, 0x00, 0x10, 0x0F, 0x04, 0x1D, 0x1E, 0x29, 0x20, 0x23, 0x08, 0x2E, 0x2C, 0x18, 0x31, 0x32, 0x01, 0x2E, 0x32, 0x2A, 0x35, 0x37, 0x3B, 0x20, 0x28, 0x42, 0x2B, 0x37, 0x53, 0x3E, 0x41, 0x03, 0x3C, 0x44, @@ -565,3 +565,9 @@ static const unsigned char testanim[] = { 0xD7, 0x82, 0xFC, 0x87, 0xAE, 0x20, 0x04, 0x10, 0xA0, 0x48, 0xA0, 0x86, 0xCC, 0x05, 0x0C, 0x30, 0x87, 0x2C, 0x38, 0x88, 0xED, 0x86, 0x04, 0x02, 0x00, 0x3B, }; + +#ifdef ROMFS_DIRENTRY_HEAD + static const ROMFS_DIRENTRY testanim_dir = { 0, 0, ROMFS_DIRENTRY_HEAD, "testanim.gif", 8938, testanim }; + #undef ROMFS_DIRENTRY_HEAD + #define ROMFS_DIRENTRY_HEAD &testanim_dir +#endif diff --git a/demos/modules/gwin/widgets/gfxconf.h b/demos/modules/gwin/widgets/gfxconf.h index b22af659..60de8d8b 100644 --- a/demos/modules/gwin/widgets/gfxconf.h +++ b/demos/modules/gwin/widgets/gfxconf.h @@ -79,6 +79,11 @@ #define GWIN_NEED_RADIO TRUE #define GWIN_NEED_LIST TRUE +/* Features for the GFILE subsystem. */ +#define GFX_USE_GFILE TRUE +#define GFILE_NEED_ROMFS TRUE +//#define GFILE_NEED_NATIVEFS TRUE + /* Features for the GINPUT subsystem. */ #define GINPUT_NEED_MOUSE TRUE diff --git a/demos/modules/gwin/widgets/main.c b/demos/modules/gwin/widgets/main.c index af2926d7..7c06f3bf 100644 --- a/demos/modules/gwin/widgets/main.c +++ b/demos/modules/gwin/widgets/main.c @@ -28,16 +28,19 @@ #include "gfx.h" -// include our chibios logo in a .gif format -#include "image_chibios.h" -#include "image_yesno.h" - /** * This demo demonstrates many of the GWIN widgets. * On the "Radio" tab try playing with the color radio buttons. * On the "Checkbox" tab try playing with the "Disable All" checkbox. */ +/** + * The image files must be stored on a GFILE file-system. + * Use either GFILE_NEED_NATIVEFS or GFILE_NEED_ROMFS (or both). + * + * The ROMFS uses the file "romfs_files.h" to describe the set of files in the ROMFS. + */ + /* Our custom yellow style */ static const GWidgetStyle YellowWidgetStyle = { Yellow, // window background @@ -184,15 +187,14 @@ static void createWidgets(void) { wi.g.x = 0+2*(LIST_WIDTH+1); wi.text = "L3"; ghList3 = gwinListCreate(0, &wi, TRUE); gwinListAddItem(ghList3, "Item 0", FALSE); gwinListAddItem(ghList3, "Item 1", FALSE); gwinListAddItem(ghList3, "Item 2", FALSE); gwinListAddItem(ghList3, "Item 3", FALSE); - gdispImageSetMemoryReader(&imgYesNo, image_yesno); - gdispImageOpen(&imgYesNo); + gdispImageOpenFile(&imgYesNo, "image_yesno.gif"); gwinListItemSetImage(ghList3, 1, &imgYesNo); gwinListItemSetImage(ghList3, 3, &imgYesNo); // Image wi.g.x = ScrWidth-210; wi.g.y = TAB_HEIGHT + 10; wi.g.width = 200; wi.g.height = 200; ghImage1 = gwinImageCreate(0, &wi.g); - gwinImageOpenMemory(ghImage1, image_chibios); + gwinImageOpenFile(ghImage1, "chibios.bmp"); gwinImageCache(ghImage1); // Console - we apply some special colors before making it visible diff --git a/demos/modules/gwin/widgets/romfs_files.h b/demos/modules/gwin/widgets/romfs_files.h new file mode 100644 index 00000000..b503765d --- /dev/null +++ b/demos/modules/gwin/widgets/romfs_files.h @@ -0,0 +1,8 @@ +/** + * This file contains the list of files for the ROMFS. + * + * The files have been converted using... + * file2c -dbcs infile outfile + */ +#include "romfs_img_chibios.h" +#include "romfs_img_yesno.h" diff --git a/demos/modules/gwin/widgets/image_chibios.h b/demos/modules/gwin/widgets/romfs_img_chibios.h similarity index 99% rename from demos/modules/gwin/widgets/image_chibios.h rename to demos/modules/gwin/widgets/romfs_img_chibios.h index a052395f..14007610 100644 --- a/demos/modules/gwin/widgets/image_chibios.h +++ b/demos/modules/gwin/widgets/romfs_img_chibios.h @@ -1,4 +1,4 @@ -static const unsigned char image_chibios[] = { +static const char image_chibios[] = { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x74, 0x00, 0x74, 0x00, 0xE7, 0xFE, 0x00, 0x08, 0x07, 0x02, 0x00, 0x0A, 0x03, 0x08, 0x07, 0x0E, 0x05, 0x0D, 0x00, 0x00, 0x11, 0x02, 0x03, 0x16, 0x01, 0x09, 0x15, 0x00, 0x00, 0x19, 0x03, 0x14, 0x16, 0x06, 0x00, 0x1D, 0x02, 0x07, 0x1C, 0x01, 0x0F, 0x1A, @@ -635,3 +635,9 @@ static const unsigned char image_chibios[] = { 0xE0, 0x3F, 0x62, 0xE0, 0x07, 0x86, 0xE0, 0x08, 0x96, 0xE0, 0x09, 0xA6, 0xE0, 0x0A, 0xB6, 0xE0, 0x0B, 0xC6, 0x60, 0xF0, 0x08, 0x08, 0x00, 0x3B, }; + +#ifdef ROMFS_DIRENTRY_HEAD + static const ROMFS_DIRENTRY image_chibios_dir = { 0, 0, ROMFS_DIRENTRY_HEAD, "chibios.bmp", 634*16+8, image_chibios }; + #undef ROMFS_DIRENTRY_HEAD + #define ROMFS_DIRENTRY_HEAD &image_chibios_dir +#endif diff --git a/demos/modules/gwin/widgets/image_yesno.h b/demos/modules/gwin/widgets/romfs_img_yesno.h similarity index 77% rename from demos/modules/gwin/widgets/image_yesno.h rename to demos/modules/gwin/widgets/romfs_img_yesno.h index c3150ea7..6b2c1290 100644 --- a/demos/modules/gwin/widgets/image_yesno.h +++ b/demos/modules/gwin/widgets/romfs_img_yesno.h @@ -1,10 +1,10 @@ /** - * This file was generated from "yesno.gif" using... + * This file was generated from "image_yesno.gif" using... * - * file2c -bcs image_yesno.gif image_yesno.h + * file2c -dcs image_yesno.gif romfs_img_yesno.h * */ -static const unsigned char image_yesno[] = { +static const char image_yesno[] = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x0B, 0x00, 0x2C, 0x00, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0x00, 0xC6, 0xFF, 0xC6, 0xCE, 0xFF, 0xCE, 0xFF, 0x08, 0x18, 0xFF, 0xCE, 0xDE, 0xFF, 0xDE, 0xDE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, @@ -19,3 +19,9 @@ static const unsigned char image_yesno[] = { 0x05, 0x35, 0x81, 0x84, 0x86, 0x83, 0x14, 0x8B, 0x07, 0x86, 0x79, 0x8F, 0x82, 0x8F, 0x8E, 0x17, 0x8E, 0x62, 0x8B, 0x8C, 0x8A, 0x87, 0x05, 0x11, 0x00, 0x3B, }; + +#ifdef ROMFS_DIRENTRY_HEAD + static const ROMFS_DIRENTRY image_yesno_dir = { 0, 0, ROMFS_DIRENTRY_HEAD, "image_yesno.gif", 202, image_yesno }; + #undef ROMFS_DIRENTRY_HEAD + #define ROMFS_DIRENTRY_HEAD &image_yesno_dir +#endif diff --git a/gfx.mk b/gfx.mk index aaea245a..0ee3e3f1 100644 --- a/gfx.mk +++ b/gfx.mk @@ -12,4 +12,4 @@ include $(GFXLIB)/src/gadc/gadc.mk include $(GFXLIB)/src/gaudin/gaudin.mk include $(GFXLIB)/src/gaudout/gaudout.mk include $(GFXLIB)/src/gmisc/gmisc.mk - +include $(GFXLIB)/src/gfile/gfile.mk diff --git a/gfxconf.example.h b/gfxconf.example.h index bb644e22..6449b58b 100644 --- a/gfxconf.example.h +++ b/gfxconf.example.h @@ -9,6 +9,8 @@ /** * Copy this file into your project directory and rename it as gfxconf.h * Edit your copy to turn on the uGFX features you want to use. + * The values below are the defaults. You should delete anything + * you are leaving as default. * * Please use spaces instead of tabs in this file. */ @@ -26,7 +28,7 @@ /////////////////////////////////////////////////////////////////////////// // GDISP // /////////////////////////////////////////////////////////////////////////// -#define GFX_USE_GDISP TRUE +#define GFX_USE_GDISP FALSE #define GDISP_NEED_AUTOFLUSH FALSE #define GDISP_NEED_TIMERFLUSH FALSE @@ -186,6 +188,27 @@ #define GINPUT_NEED_DIAL FALSE +/////////////////////////////////////////////////////////////////////////// +// GFILE // +/////////////////////////////////////////////////////////////////////////// +#define GFX_USE_GFILE FALSE + +#define GFILE_NEED_PRINTG FALSE +#define GFILE_NEED_SCANG FALSE +#define GFILE_NEED_STRINGS FALSE +#define GFILE_NEED_STDIO FALSE + #define GFILE_ALLOW_FLOATS FALSE + #define GFILE_ALLOW_DEVICESPECIFIC FALSE + #define GFILE_MAX_GFILES 3 + +#define GFILE_NEED_MEMFS FALSE +#define GFILE_NEED_ROMFS FALSE +#define GFILE_NEED_RAMFS FALSE +#define GFILE_NEED_FATFS FALSE +#define GFILE_NEED_NATIVEFS FALSE +#define GFILE_NEED_CHBIOSFS FALSE + + /////////////////////////////////////////////////////////////////////////// // GADC // /////////////////////////////////////////////////////////////////////////// diff --git a/include/gdisp/image.h b/include/gdisp/image.h index ff2e9d85..607f1007 100644 --- a/include/gdisp/image.h +++ b/include/gdisp/image.h @@ -40,6 +40,7 @@ typedef uint16_t gdispImageError; #define GDISP_IMAGE_ERR_UNSUPPORTED (GDISP_IMAGE_ERR_UNRECOVERABLE+3) #define GDISP_IMAGE_ERR_UNSUPPORTED_OK 3 #define GDISP_IMAGE_ERR_NOMEMORY (GDISP_IMAGE_ERR_UNRECOVERABLE+4) + #define GDISP_IMAGE_ERR_NOSUCHFILE (GDISP_IMAGE_ERR_UNRECOVERABLE+5) /** * @brief Image flags @@ -103,10 +104,10 @@ typedef struct gdispImage { gdispImageFlags flags; /* @< The image flags */ color_t bgcolor; /* @< The default background color */ coord_t width, height; /* @< The image dimensions */ - gdispImageIO io; /* @< The image IO functions */ + GFILE * f; /* @< The underlying GFILE */ #if GDISP_NEED_IMAGE_ACCOUNTING - uint32_t memused; /* @< How much RAM is currently allocated */ - uint32_t maxmemused; /* @< How much RAM has been allocated (maximum) */ + uint32_t memused; /* @< How much RAM is currently allocated */ + uint32_t maxmemused; /* @< How much RAM has been allocated (maximum) */ #endif const struct gdispImageHandlers * fns; /* @< Don't mess with this! */ struct gdispImagePrivate * priv; /* @< Don't mess with this! */ @@ -116,59 +117,28 @@ typedef struct gdispImage { extern "C" { #endif - /** - * @brief Sets the io fields in the image structure to routines - * that support reading from an image stored in RAM or Flash. - * - * @return TRUE if the IO open function succeeds - * - * @param[in] img The image structure - * @param[in] memimage A pointer to the image in RAM or Flash - * - * @note Always returns TRUE for a Memory Reader + /* + * Deprecated Functions. */ - bool_t gdispImageSetMemoryReader(gdispImage *img, const void *memimage); - - #if GFX_USE_OS_CHIBIOS || defined(__DOXYGEN__) - /** - * @brief Sets the io fields in the image structure to routines - * that support reading from an image stored on a BaseFileStream (eg SDCard). - * - * @return TRUE if the IO open function succeeds - * - * @param[in] img The image structure - * @param[in] BaseFileStreamPtr A pointer to the (open) BaseFileStream object. - * - */ - bool_t gdispImageSetBaseFileStreamReader(gdispImage *img, void *BaseFileStreamPtr); + gdispImageError DEPRECATED("Use gdispImageOpenGFile() instead") gdispImageOpen(gdispImage *img); + bool_t DEPRECATED("Use gdispImageOpenMemory() instead") gdispImageSetMemoryReader(gdispImage *img, const void *memimage); + #if GFX_USE_OS_CHIBIOS + bool_t DEPRECATED("Use gdispImageOpenBaseFileStream() instead") gdispImageSetBaseFileStreamReader(gdispImage *img, void *BaseFileStreamPtr); #endif - - #if defined(WIN32) || GFX_USE_OS_WIN32 || GFX_USE_OS_LINUX || GFX_USE_OS_OSX || defined(__DOXYGEN__) - /** - * @brief Sets the io fields in the image structure to routines - * that support reading from an image stored in Win32 simulators native - * file system. - * @pre Only available on the Win32 simulator - * - * @return TRUE if the IO open function succeeds - * - * @param[in] img The image structure - * @param[in] filename The filename to open - * - */ - bool_t gdispImageSetFileReader(gdispImage *img, const char *filename); - /* Old definition */ + #if defined(WIN32) || GFX_USE_OS_WIN32 || GFX_USE_OS_LINUX || GFX_USE_OS_OSX + bool_t DEPRECATED("Please use gdispImageOpenFile() instead") gdispImageSetFileReader(gdispImage *img, const char *filename); #define gdispImageSetSimulFileReader(img, fname) gdispImageSetFileReader(img, fname) #endif - + /** - * @brief Open an image ready for drawing + * @brief Open an image using an open GFILE and get it ready for drawing * @details Determine the image format and get ready to decode the first image frame * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. - * - * @param[in] img The image structure - * - * @pre The io fields should be filled in before calling gdispImageOpen() + * + * @param[in] img The image structure + * @param[in] f The open GFILE stream. + * + * @pre The GFILE must be open for reading. * * @note This determines which decoder to use and then initialises all other fields * in the gdispImage structure. @@ -179,17 +149,62 @@ extern "C" { * bit in the error code. * A partial success return code means an image can still be drawn but perhaps with * reduced functionality eg only the first page of a multi-page image. - * @note @p gdispImageClose() can be called even after a failure to open the image to ensure - * that the IO close routine gets called. + * @note @p gdispImageClose() should be called when finished with the image. This will close + * the image and its underlying GFILE file. Note that images opened with partial success + * (eg GDISP_IMAGE_ERR_UNSUPPORTED_OK) + * still need to be closed when you are finished with them. */ - gdispImageError gdispImageOpen(gdispImage *img); - + gdispImageError gdispImageOpenGFile(gdispImage *img, GFILE *f); + + /** + * @brief Open an image in a file and get it ready for drawing + * @details Determine the image format and get ready to decode the first image frame + * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. + * + * @pre You must have included the file-system support into GFILE that you want to use. + * + * @param[in] img The image structure + * @param[in] filename The filename to open + * + * @note This function just opens the GFILE using the filename and passes it to @p gdispImageOpenGFile(). + */ + #define gdispImageOpenFile(img, filename) gdispImageOpenGFile((img), gfileOpen((filename), "rb")) + + /** + * @brief Open an image in a ChibiOS basefilestream and get it ready for drawing + * @details Determine the image format and get ready to decode the first image frame + * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. + * + * @pre GFILE_NEED_CHIBIOSFS and GFX_USE_OS_CHIBIOS must be TRUE. This only makes sense on the ChibiOS + * operating system. + * + * @param[in] img The image structure + * @param[in] BaseFileStreamPtr A pointer to an open BaseFileStream + * + * @note This function just opens the GFILE using the basefilestream and passes it to @p gdispImageOpenGFile(). + */ + #define gdispImageOpenBaseFileStream(img, BaseFileStreamPtr) gdispImageOpenGFile((img), gfileOpenBaseFileStream((BaseFileStreamPtr), "rb")) + + /** + * @brief Open an image in memory and get it ready for drawing + * @details Determine the image format and get ready to decode the first image frame + * @return GDISP_IMAGE_ERR_OK (0) on success or an error code. + * + * @pre GFILE_NEED_MEMFS must be TRUE + * + * @param[in] img The image structure + * @param[in] ptr A pointer to the image bytes in memory + * + * @note This function just opens the GFILE using the basefilestream and passes it to @p gdispImageOpenGFile(). + */ + #define gdispImageOpenMemory(img, ptr) gdispImageOpenGFile((img), gfileOpenMemory((void *)(ptr), "rb")) + /** * @brief Close an image and release any dynamically allocated working storage. * * @param[in] img The image structure * - * @pre gdispImageOpen() must have returned successfully. + * @pre gdispImageOpenFile() must have returned successfully. * * @note Also calls the IO close function (if it hasn't already been called). */ @@ -282,94 +297,6 @@ extern "C" { */ delaytime_t gdispImageNext(gdispImage *img); - #if GDISP_NEED_IMAGE_NATIVE - /** - * @brief The image drawing routines for a NATIVE format image. - * - * @note Only use these functions if you absolutely know the format - * of the image you are decoding. Generally you should use the - * generic functions and it will auto-detect the format. - * @note A NATIVE format image is defined as an 8 byte header described below, immediately - * followed by the bitmap data. The bitmap data is stored in the native format for - * the display controller. If the pixel format specified in the header does not - * match the controller native format then the image is rejected. - * @note The 8 byte header: - * { 'N', 'I', width.hi, width.lo, height.hi, height.lo, format.hi, format.lo } - * The format word = GDISP_PIXELFORMAT - * @{ - */ - gdispImageError gdispImageOpen_NATIVE(gdispImage *img); - void gdispImageClose_NATIVE(gdispImage *img); - gdispImageError gdispImageCache_NATIVE(gdispImage *img); - gdispImageError gdispGImageDraw_NATIVE(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); - delaytime_t gdispImageNext_NATIVE(gdispImage *img); - /* @} */ - #endif - - #if GDISP_NEED_IMAGE_GIF - /** - * @brief The image drawing routines for a GIF image. - * @note Only use these functions if you absolutely know the format - * of the image you are decoding. Generally you should use the - * generic functions and it will auto-detect the format. - * @{ - */ - gdispImageError gdispImageOpen_GIF(gdispImage *img); - void gdispImageClose_GIF(gdispImage *img); - gdispImageError gdispImageCache_GIF(gdispImage *img); - gdispImageError gdispGImageDraw_GIF(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); - delaytime_t gdispImageNext_GIF(gdispImage *img); - /* @} */ - #endif - - #if GDISP_NEED_IMAGE_BMP - /** - * @brief The image drawing routines for a BMP image. - * @note Only use these functions if you absolutely know the format - * of the image you are decoding. Generally you should use the - * generic functions and it will auto-detect the format. - * @{ - */ - gdispImageError gdispImageOpen_BMP(gdispImage *img); - void gdispImageClose_BMP(gdispImage *img); - gdispImageError gdispImageCache_BMP(gdispImage *img); - gdispImageError gdispGImageDraw_BMP(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); - delaytime_t gdispImageNext_BMP(gdispImage *img); - /* @} */ - #endif - - #if GDISP_NEED_IMAGE_JPG - /** - * @brief The image drawing routines for a JPG image. - * @note Only use these functions if you absolutely know the format - * of the image you are decoding. Generally you should use the - * generic functions and it will auto-detect the format. - * @{ - */ - gdispImageError gdispImageOpen_JPG(gdispImage *img); - void gdispImageClose_JPG(gdispImage *img); - gdispImageError gdispImageCache_JPG(gdispImage *img); - gdispImageError gdispGImageDraw_JPG(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); - delaytime_t gdispImageNext_JPG(gdispImage *img); - /* @} */ - #endif - - #if GDISP_NEED_IMAGE_PNG - /** - * @brief The image drawing routines for a PNG image. - * @note Only use these functions if you absolutely know the format - * of the image you are decoding. Generally you should use the - * generic functions and it will auto-detect the format. - * @{ - */ - gdispImageError gdispImageOpen_PNG(gdispImage *img); - void gdispImageClose_PNG(gdispImage *img); - gdispImageError gdispImageCache_PNG(gdispImage *img); - gdispImageError gdispGImageDraw_PNG(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); - delaytime_t gdispImageNext_PNG(gdispImage *img); - /* @} */ - #endif - #ifdef __cplusplus } #endif diff --git a/include/gfile/gfile.h b/include/gfile/gfile.h new file mode 100644 index 00000000..62972c47 --- /dev/null +++ b/include/gfile/gfile.h @@ -0,0 +1,169 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file include/gfile/gfile.h + * @brief GFILE - File IO Routines header file. + * + * @addtogroup GFILE + * + * @brief Module which contains Operating system independent FILEIO + * + * @{ + */ + +#ifndef _GFILE_H +#define _GFILE_H + +#include "gfx.h" + +#if GFX_USE_GFILE || defined(__DOXYGEN__) + +/*===========================================================================*/ +/* Type definitions */ +/*===========================================================================*/ + +/** + * @brief A file pointer + */ + +#ifndef GFILE_IMPLEMENTATION + typedef void GFILE; +#else + typedef struct GFILE GFILE; +#endif + +extern GFILE *gfileStdIn; +extern GFILE *gfileStdErr; +extern GFILE *gfileStdOut; + +/*===========================================================================*/ +/* External declarations. */ +/*===========================================================================*/ + +#ifdef __cplusplus +extern "C" { +#endif + + bool_t gfileExists(const char *fname); + bool_t gfileDelete(const char *fname); + long int gfileGetFilesize(const char *fname); + bool_t gfileRename(const char *oldname, const char *newname); + GFILE * gfileOpen(const char *fname, const char *mode); + void gfileClose(GFILE *f); + size_t gfileRead(GFILE *f, void *buf, size_t len); + size_t gfileWrite(GFILE *f, const void *buf, size_t len); + long int gfileGetPos(GFILE *f); + bool_t gfileSetPos(GFILE *f, long int pos); + long int gfileGetSize(GFILE *f); + bool_t gfileEOF(GFILE *f); + + #if GFILE_NEED_CHIBIOSFS && GFX_USE_OS_CHIBIOS + GFILE * gfileOpenBaseFileStream(void *BaseFileStreamPtr, const char *mode); + #endif + #if GFILE_NEED_MEMFS + GFILE * gfileOpenMemory(void *memptr, const char *mode); + #endif + + #if GFILE_NEED_PRINTG + int vfnprintg(GFILE *f, int maxlen, const char *fmt, va_list arg); + int fnprintg(GFILE *f, int maxlen, const char *fmt, ...); + #define vfprintg(f,m,a) vfnprintg(f,0,m,a) + #define fprintg(f,m,...) fnprintg(f,0,m,...) + #define vprintg(m,a) vfnprintg(gfileStdOut,0,m,a) + #define printg(m,...) fnprintg(gfileStdOut,0,m,...) + + #if GFILE_NEED_STRINGS + int vsnprintg(char *buf, int maxlen, const char *fmt, va_list arg); + int snprintg(char *buf, int maxlen, const char *fmt, ...); + #define vsprintg(s,m,a) vsnprintg(s,0,m,a) + #define sprintg(s,m,...) snprintg(s,0,m,...) + #endif + #endif + + #if GFILE_NEED_SCANG + int vfscang(GFILE *f, const char *fmt, va_list arg); + int fscang(GFILE *f, const char *fmt, ...); + #define vscang(f,a) vfscang(gfileStdIn,f,a) + #define scang(f,...) fscang(gfileStdIn,f,...) + + #if GFILE_NEED_STRINGS + int vsscang(const char *buf, const char *fmt, va_list arg); + int sscang(const char *buf, const char *fmt, ...); + #endif + #endif + + #if GFILE_NEED_STDIO && !defined(GFILE_IMPLEMENTATION) + #define stdin gfileStdIn + #define stdout gfileStdOut + #define stderr gfileStdErr + #define FILENAME_MAX 256 // Use a relatively small number for an embedded platform + #define L_tmpnam FILENAME_MAX + #define FOPEN_MAX GFILE_MAX_GFILES + #define TMP_MAX GFILE_MAX_GFILES + #define P_tmpdir "/tmp/" + #define FILE GFILE + #define fopen(n,m) gfileOpen(n,m) + #define fclose(f) gfileClose(f) + size_t gstdioRead(void * ptr, size_t size, size_t count, FILE *f); + size_t gstdioWrite(const void * ptr, size_t size, size_t count, FILE *f); + #define fread(p,sz,cnt,f) gstdioRead(p,sz,cnt,f) + #define fwrite(p,sz,cnt,f) gstdioWrite(p,sz,cnt,f) + int gstdioSeek(FILE *f, size_t offset, int origin); + #define fseek(f,ofs,org) gstdioSeek(f,ofs,org) + #define SEEK_SET 0 + #define SEEK_CUR 1 + #define SEEK_END 2 + #define remove(n) (!gfileDelete(n)) + #define rename(o,n) (!gfileRename(o,n)) + #define fflush(f) (0) + #define ftell(f) gfileGetPos(f) + #define fpos_t long int + int gstdioGetpos(FILE *f, long int *pos); + #define fgetpos(f,pos) gstdioGetpos(f,pos) + #define fsetpos(f, pos) (!gfileSetPos(f, *pos)) + #define rewind(f) gfileSetPos(f, 0); + #define feof(f) gfileEOF(f) + + #define vfprintf(f,m,a) vfnprintg(f,0,m,a) + #define fprintf(f,m,...) fnprintg(f,0,m,...) + #define vprintf(m,a) vfnprintg(gfileStdOut,0,m,a) + #define printf(m,...) fnprintg(gfileStdOut,0,m,...) + #define vsnprintf(s,n,m,a) vsnprintg(s,n,m,a) + #define snprintf(s,n,m,...) snprintg(s,n,m,...) + #define vsprintf(s,m,a) vsnprintg(s,0,m,a) + #define sprintf(s,m,...) snprintg(s,0,m,...) + //TODO + //void clearerr ( FILE * stream ); + //int ferror ( FILE * stream ); + //FILE * tmpfile ( void ); // Auto-deleting + //char * tmpnam ( char * str ); + //char * mktemp (char *template); + //FILE * freopen ( const char * filename, const char * mode, FILE * stream ); + //setbuf + //setvbuf + //fflush + //fgetc + //fgets + //fputc + //fputs + //getc + //getchar + //puts + //ungetc + //void perror (const char * str); + #endif + +#ifdef __cplusplus +} +#endif + +#endif /* GFX_USE_GFILE */ + +#endif /* _GFILE_H */ +/** @} */ + diff --git a/include/gfile/options.h b/include/gfile/options.h new file mode 100644 index 00000000..d73af02c --- /dev/null +++ b/include/gfile/options.h @@ -0,0 +1,158 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file include/gfile/options.h + * @brief GFILE - File IO options header file. + * + * @addtogroup GFILE + * @{ + */ + +#ifndef _GFILE_OPTIONS_H +#define _GFILE_OPTIONS_H + +/** + * @name GFILE Functionality to be included + * @{ + */ + /** + * @brief Include printg, fprintg etc functions + * @details Defaults to FALSE + */ + #ifndef GFILE_NEED_PRINTG + #define GFILE_NEED_PRINTG FALSE + #endif + /** + * @brief Include scang, fscang etc functions + * @details Defaults to FALSE + */ + #ifndef GFILE_NEED_SCANG + #define GFILE_NEED_SCANG FALSE + #endif + /** + * @brief Include the string sprintg/sscang functions + * @details Defaults to FALSE + * @pre To get sprintg functions you also need to define @p GFILE_NEED_PRINTG + * @pre To get sscang functions you also need to define @p GFILE_NEED_SCANG + */ + #ifndef GFILE_NEED_STRINGS + #define GFILE_NEED_STRINGS FALSE + #endif + /** + * @brief Map many stdio functions to their GFILE equivalent + * @details Defaults to FALSE + * @note This replaces the functions in stdio.h with equivalents + * - Do not include stdio.h as it has different conflicting definitions. + */ + #ifndef GFILE_NEED_STDIO + #define GFILE_NEED_STDIO FALSE + #endif + /** + * @brief Include the ROM file system + * @details Defaults to FALSE + * @note If GFILE_ALLOW_DEVICESPECIFIC is on then you can ensure that you are + * opening a file on the ROM file system by prefixing + * its name with "S|" (the letter 'S', followed by a vertical bar). + * @note This requires a file called romfs_files.h to be in the + * users project include path. This file should include all the files + * converted to .h files using the file2c utility (using flags "-dbcs"). + */ + #ifndef GFILE_NEED_ROMFS + #define GFILE_NEED_ROMFS FALSE + #endif + /** + * @brief Include the RAM file system + * @details Defaults to FALSE + * @note If GFILE_ALLOW_DEVICESPECIFIC is on then you can ensure that you are + * opening a file on the RAM file system by prefixing + * its name with "R|" (the letter 'R', followed by a vertical bar). + * @note You must also define GFILE_RAMFS_SIZE with the size of the file system + * to be allocated in RAM. + */ + #ifndef GFILE_NEED_RAMFS + #define GFILE_NEED_RAMFS FALSE + #endif + /** + * @brief Include the FAT file system driver + * @details Defaults to FALSE + * @note If GFILE_ALLOW_DEVICESPECIFIC is on then you can ensure that you are + * opening a file on the FAT file system by prefixing + * its name with "F|" (the letter 'F', followed by a vertical bar). + * @note You must separately include the FATFS library and code. + */ + #ifndef GFILE_NEED_FATFS + #define GFILE_NEED_FATFS FALSE + #endif + /** + * @brief Include the operating system's native file system + * @details Defaults to FALSE + * @note If GFILE_ALLOW_DEVICESPECIFIC is on then you can ensure that you are + * opening a file on the native file system by prefixing + * its name with "N|" (the letter 'N', followed by a vertical bar). + * @note If defined then the gfileStdOut and gfileStdErr handles + * use the operating system equivalent stdio and stderr. + * If it is not defined the gfileStdOut and gfileStdErr io is discarded. + */ + #ifndef GFILE_NEED_NATIVEFS + #define GFILE_NEED_NATIVEFS FALSE + #endif + /** + * @brief Include ChibiOS BaseFileStream support + * @details Defaults to FALSE + * @pre This is only relevant on the ChibiOS operating system. + * @note Use the @p gfileOpenBaseFileStream() call to open a GFILE based on a + * BaseFileStream. The BaseFileStream must already be open. + * @note A GFile of this type cannot be opened by filename. The BaseFileStream + * must be pre-opened using the operating system. + */ + #ifndef GFILE_NEED_CHIBIOSFS + #define GFILE_NEED_CHIBIOSFS FALSE + #endif + /** + * @brief Include raw memory pointer support + * @details Defaults to FALSE + * @note Use the @p gfileOpenMemory() call to open a GFILE based on a + * memory pointer. The GFILE opened appears to be of unlimited size. + * @note A GFile of this type cannot be opened by filename. + */ + #ifndef GFILE_NEED_MEMFS + #define GFILE_NEED_MEMFS FALSE + #endif +/** + * @} + * + * @name GFILE Optional Parameters + * @{ + */ + /** + * @brief Add floating point support to printg/scang etc. + */ + #ifndef GFILE_ALLOW_FLOATS + #define GFILE_ALLOW_FLOATS + #endif + /** + * @brief Can the device be specified as part of the file name. + * @note If this is on then a device letter and a vertical bar can be + * prefixed on a file name to specify that it must be on a + * specific device. + */ + #ifndef GFILE_ALLOW_DEVICESPECIFIC + #define GFILE_ALLOW_DEVICESPECIFIC FALSE + #endif + /** + * @brief The maximum number of open files + * @note This count excludes gfileStdIn, gfileStdOut and gfileStdErr + * (if open by default). + */ + #ifndef GFILE_MAX_GFILES + #define GFILE_MAX_GFILES 3 + #endif +/** @} */ + +#endif /* _GFILE_OPTIONS_H */ +/** @} */ diff --git a/include/gfx.h b/include/gfx.h index d261a6ce..0c922669 100644 --- a/include/gfx.h +++ b/include/gfx.h @@ -150,6 +150,13 @@ #ifndef GFX_USE_GMISC #define GFX_USE_GMISC FALSE #endif + /** + * @brief GFX File API + * @details Defaults to FALSE + */ + #ifndef GFX_USE_GFILE + #define GFX_USE_GFILE FALSE + #endif /** @} */ /** @@ -157,6 +164,7 @@ * */ #include "gos/options.h" +#include "gfile/options.h" #include "gmisc/options.h" #include "gqueue/options.h" #include "gevent/options.h" @@ -169,7 +177,7 @@ #include "gaudout/options.h" /** - * Inter-dependancy safety checks on the sub-systems. + * Interdependency safety checks on the sub-systems. * */ #include "gfx_rules.h" @@ -178,6 +186,7 @@ * Include the sub-system header files */ #include "gos/gos.h" +#include "gfile/gfile.h" #include "gmisc/gmisc.h" #include "gqueue/gqueue.h" #include "gevent/gevent.h" @@ -208,7 +217,6 @@ extern "C" { * @brief The one call to end it all * * @note This will deinitialise each sub-system that has been turned on. - * @note Do not call this without a previous @p gfxInit(); * * @api */ diff --git a/include/gfx_rules.h b/include/gfx_rules.h index d547f923..6a8f5ab1 100644 --- a/include/gfx_rules.h +++ b/include/gfx_rules.h @@ -205,6 +205,15 @@ #undef GDISP_INCLUDE_FONT_UI2 #define GDISP_INCLUDE_FONT_UI2 TRUE #endif + #if GDISP_NEED_IMAGE + #if !GFX_USE_GFILE + #if GFX_DISPLAY_RULE_WARNINGS + #warning "GDISP: GFX_USE_GFILE is required when GDISP_NEED_IMAGE is TRUE. It has been turned on for you." + #endif + #undef GFX_USE_GFILE + #define GFX_USE_GFILE TRUE + #endif + #endif #endif #if GFX_USE_GAUDIN @@ -248,5 +257,8 @@ #if GFX_USE_GMISC #endif +#if GFX_USE_GFILE +#endif + #endif /* _GFX_H */ /** @} */ diff --git a/include/gwin/image.h b/include/gwin/image.h index 66dd0b94..66aba3d1 100644 --- a/include/gwin/image.h +++ b/include/gwin/image.h @@ -60,42 +60,51 @@ GHandle gwinGImageCreate(GDisplay *g, GImageObject *widget, GWindowInit *pInit); #define gwinImageCreate(w, pInit) gwinGImageCreate(GDISP, w, pInit) /** - * @brief Sets the input routines that support reading the image from memory - * in RAM or flash. - * @return TRUE if the IO open function succeeds + * @brief Opens the image using a GFILE + * @return TRUE if the image can be opened * * @param[in] gh The widget (must be an image widget) - * @param[in] memory A pointer to the image in RAM or Flash + * @param[in] f The open (for reading) GFILE to use * * @api */ -bool_t gwinImageOpenMemory(GHandle gh, const void* memory); +bool_t gwinImageOpenGFile(GHandle gh, GFILE *f); + +/** + * @brief Opens the image using the specified filename + * @return TRUE if the open succeeds + * + * @param[in] gh The widget (must be an image widget) + * @param[in] filename The filename to open + * + * @api + */ +#define gwinImageOpenFile(gh, filename) gwinImageOpenGFile((gh), gfileOpen((filename), "rb")) -#if defined(WIN32) || GFX_USE_OS_WIN32 || GFX_USE_OS_LINUX || GFX_USE_OS_OSX || defined(__DOXYGEN__) /** - * @brief Sets the input routines that support reading the image from a file + * @brief Sets the input routines that support reading the image from memory + * in RAM or flash. + * @pre GFILE_NEED_MEMFS must be TRUE * @return TRUE if the IO open function succeeds * * @param[in] gh The widget (must be an image widget) - * @param[in] filename The filename to open + * @param[in] ptr A pointer to the image in RAM or Flash * * @api */ - bool_t gwinImageOpenFile(GHandle gh, const char* filename); -#endif +#define gwinImageOpenMemory(gh, ptr) gwinImageOpenGFile((gh), gfileOpenMemory((void *)(ptr), "rb")) -#if GFX_USE_OS_CHIBIOS || defined(__DOXYGEN__) - /** - * @brief Sets the input routines that support reading the image from a BaseFileStream (eg. an SD-Card). - * @return TRUE if the IO open function succeeds - * - * @param[in] gh The widget (must be an image widget) - * @param[in] streamPtr A pointer to the (open) BaseFileStream object. - * - * @api - */ - bool_t gwinImageOpenStream(GHandle gh, void *streamPtr); -#endif +/** + * @brief Sets the input routines that support reading the image from a BaseFileStream (eg. an SD-Card). + * @return TRUE if the IO open function succeeds + * @pre GFILE_NEED_CHIBIOSFS and GFX_USE_OS_CHIBIOS must be TRUE + * + * @param[in] gh The widget (must be an image widget) + * @param[in] streamPtr A pointer to the (open) BaseFileStream object. + * + * @api + */ +#define gwinImageOpenStream(gh, streamPtr) gwinImageOpenGFile((gh), gfileOpenBaseFIleStream((streamPtr), "rb")) /** * @brief Cache the image. diff --git a/src/gdisp/image.c b/src/gdisp/image.c index 62af0aeb..2b8395b0 100644 --- a/src/gdisp/image.c +++ b/src/gdisp/image.c @@ -16,7 +16,45 @@ #if GFX_USE_GDISP && GDISP_NEED_IMAGE -#include +#if GDISP_NEED_IMAGE_NATIVE + extern gdispImageError gdispImageOpen_NATIVE(gdispImage *img); + extern void gdispImageClose_NATIVE(gdispImage *img); + extern gdispImageError gdispImageCache_NATIVE(gdispImage *img); + extern gdispImageError gdispGImageDraw_NATIVE(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); + extern delaytime_t gdispImageNext_NATIVE(gdispImage *img); +#endif + +#if GDISP_NEED_IMAGE_GIF + extern gdispImageError gdispImageOpen_GIF(gdispImage *img); + extern void gdispImageClose_GIF(gdispImage *img); + extern gdispImageError gdispImageCache_GIF(gdispImage *img); + extern gdispImageError gdispGImageDraw_GIF(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); + extern delaytime_t gdispImageNext_GIF(gdispImage *img); +#endif + +#if GDISP_NEED_IMAGE_BMP + extern gdispImageError gdispImageOpen_BMP(gdispImage *img); + extern void gdispImageClose_BMP(gdispImage *img); + extern gdispImageError gdispImageCache_BMP(gdispImage *img); + extern gdispImageError gdispGImageDraw_BMP(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); + extern delaytime_t gdispImageNext_BMP(gdispImage *img); +#endif + +#if GDISP_NEED_IMAGE_JPG + extern gdispImageError gdispImageOpen_JPG(gdispImage *img); + extern void gdispImageClose_JPG(gdispImage *img); + extern gdispImageError gdispImageCache_JPG(gdispImage *img); + extern gdispImageError gdispGImageDraw_JPG(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); + extern delaytime_t gdispImageNext_JPG(gdispImage *img); +#endif + +#if GDISP_NEED_IMAGE_PNG + extern gdispImageError gdispImageOpen_PNG(gdispImage *img); + extern void gdispImageClose_PNG(gdispImage *img); + extern gdispImageError gdispImageCache_PNG(gdispImage *img); + extern gdispImageError gdispGImageDraw_PNG(GDisplay *g, gdispImage *img, coord_t x, coord_t y, coord_t cx, coord_t cy, coord_t sx, coord_t sy); + extern delaytime_t gdispImageNext_PNG(gdispImage *img); +#endif /* The structure defining the routines for image drawing */ typedef struct gdispImageHandlers { @@ -59,134 +97,76 @@ static gdispImageHandlers ImageHandlers[] = { #endif }; -static size_t ImageMemoryRead(struct gdispImageIO *pio, void *buf, size_t len) { - if (pio->fd == (void *)-1) return 0; - memcpy(buf, ((const char *)pio->fd)+pio->pos, len); - pio->pos += len; - return len; +gdispImageError + DEPRECATED("Use gdispImageOpenGFile() instead") + gdispImageOpen(gdispImage *img) { + return gdispImageOpenGFile(img, img->f); } -static void ImageMemorySeek(struct gdispImageIO *pio, size_t pos) { - if (pio->fd == (void *)-1) return; - pio->pos = pos; -} - -static void ImageMemoryClose(struct gdispImageIO *pio) { - pio->fd = (void *)-1; - pio->pos = 0; -} - -static const gdispImageIOFunctions ImageMemoryFunctions = - { ImageMemoryRead, ImageMemorySeek, ImageMemoryClose }; - -bool_t gdispImageSetMemoryReader(gdispImage *img, const void *memimage) { - img->io.fns = &ImageMemoryFunctions; - img->io.pos = 0; - img->io.fd = memimage; - return TRUE; -} - -#if GFX_USE_OS_CHIBIOS - static size_t ImageBaseFileStreamRead(struct gdispImageIO *pio, void *buf, size_t len) { - if (pio->fd == (void *)-1) return 0; - len = chSequentialStreamRead(((BaseFileStream *)pio->fd), (uint8_t *)buf, len); - pio->pos += len; - return len; - } - - static void ImageBaseFileStreamSeek(struct gdispImageIO *pio, size_t pos) { - if (pio->fd == (void *)-1) return; - if (pio->pos != pos) { - chFileStreamSeek(((BaseFileStream *)pio->fd), pos); - pio->pos = pos; - } - } - - static void ImageBaseFileStreamClose(struct gdispImageIO *pio) { - if (pio->fd == (void *)-1) return; - chFileStreamClose(((BaseFileStream *)pio->fd)); - pio->fd = (void *)-1; - pio->pos = 0; - } - - static const gdispImageIOFunctions ImageBaseFileStreamFunctions = - { ImageBaseFileStreamRead, ImageBaseFileStreamSeek, ImageBaseFileStreamClose }; - - bool_t gdispImageSetBaseFileStreamReader(gdispImage *img, void *BaseFileStreamPtr) { - img->io.fns = &ImageBaseFileStreamFunctions; - img->io.pos = 0; - img->io.fd = BaseFileStreamPtr; - return TRUE; +#if GFILE_NEED_MEMFS + bool_t + DEPRECATED("Use gdispImageOpenMemory() instead") + gdispImageSetMemoryReader(gdispImage *img, const void *memimage) { + img->f = gfileOpenMemory((void *)memimage, "rb"); + return img->f != 0; } #endif #if defined(WIN32) || GFX_USE_OS_WIN32 || GFX_USE_OS_LINUX || GFX_USE_OS_OSX - #include - - static size_t ImageFileRead(struct gdispImageIO *pio, void *buf, size_t len) { - if (!pio->fd) return 0; - len = fread(buf, 1, len, (FILE *)pio->fd); - if ((int)len < 0) len = 0; - pio->pos += len; - return len; - } - - static void ImageFileSeek(struct gdispImageIO *pio, size_t pos) { - if (!pio->fd) return; - if (pio->pos != pos) { - fseek((FILE *)pio->fd, pos, SEEK_SET); - pio->pos = pos; - } - } - - static void ImageFileClose(struct gdispImageIO *pio) { - if (!pio->fd) return; - fclose((FILE *)pio->fd); - pio->fd = 0; - pio->pos = 0; - } - - static const gdispImageIOFunctions ImageFileFunctions = - { ImageFileRead, ImageFileSeek, ImageFileClose }; - - bool_t gdispImageSetFileReader(gdispImage *img, const char *filename) { - img->io.fns = &ImageFileFunctions; - img->io.pos = 0; - #if defined(WIN32) || GFX_USE_OS_WIN32 - img->io.fd = (void *)fopen(filename, "rb"); - #else - img->io.fd = (void *)fopen(filename, "r"); - #endif - - return img->io.fd != 0; + bool_t + DEPRECATED("Use gdispImageOpenFile() instead") + gdispImageSetFileReader(gdispImage *img, const char *filename) { + img->f = gfileOpen(filename, "rb"); + return img->f != 0; } #endif -gdispImageError gdispImageOpen(gdispImage *img) { +#if GFILE_NEED_CHIBIOSFS && GFX_USE_OS_CHIBIOS + bool_t + DEPRECATED("Use gdispImageOpenBaseFileStream() instead") + gdispImageSetBaseFileStreamReader(gdispImage *img, void *BaseFileStreamPtr) { + img->f = gfileOpenBaseFileStream(BaseFileStreamPtr, "rb"); + return img->f != 0; + } +#endif + +gdispImageError gdispImageOpenGFile(gdispImage *img, GFILE *f) { gdispImageError err; + if (!f) + return GDISP_IMAGE_ERR_NOSUCHFILE; + img->f = f; img->bgcolor = White; for(img->fns = ImageHandlers; img->fns < ImageHandlers+sizeof(ImageHandlers)/sizeof(ImageHandlers[0]); img->fns++) { err = img->fns->open(img); if (err != GDISP_IMAGE_ERR_BADFORMAT) { if ((err & GDISP_IMAGE_ERR_UNRECOVERABLE)) - img->fns = 0; + goto unrecoverable; + + // Everything is possible return err; } - img->io.fns->seek(&img->io, 0); + + // Try the next decoder + gfileSetPos(img->f, 0); } + + err = GDISP_IMAGE_ERR_BADFORMAT; img->type = GDISP_IMAGE_TYPE_UNKNOWN; + +unrecoverable: + gfileClose(img->f); + img->f = 0; img->flags = 0; img->fns = 0; img->priv = 0; - return GDISP_IMAGE_ERR_BADFORMAT; + return err; } void gdispImageClose(gdispImage *img) { if (img->fns) img->fns->close(img); - else - img->io.fns->close(&img->io); + gfileClose(img->f); img->type = GDISP_IMAGE_TYPE_UNKNOWN; img->flags = 0; img->fns = 0; @@ -194,7 +174,7 @@ void gdispImageClose(gdispImage *img) { } bool_t gdispImageIsOpen(gdispImage *img) { - return img->type != GDISP_IMAGE_TYPE_UNKNOWN && img->fns != 0; + return img->fns != 0; } void gdispImageSetBgColor(gdispImage *img, color_t bgcolor) { diff --git a/src/gdisp/image_bmp.c b/src/gdisp/image_bmp.c index 158d6edc..8ff40ca0 100644 --- a/src/gdisp/image_bmp.c +++ b/src/gdisp/image_bmp.c @@ -118,6 +118,19 @@ typedef struct gdispImagePrivate { pixel_t buf[BLIT_BUFFER_SIZE]; } gdispImagePrivate; +void gdispImageClose_BMP(gdispImage *img) { + if (img->priv) { +#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE + if (img->priv->palette) + gdispImageFree(img, (void *)img->priv->palette, img->priv->palsize*sizeof(color_t)); +#endif + if (img->priv->frame0cache) + gdispImageFree(img, (void *)img->priv->frame0cache, img->width*img->height*sizeof(pixel_t)); + gdispImageFree(img, (void *)img->priv, sizeof(gdispImagePrivate)); + img->priv = 0; + } +} + gdispImageError gdispImageOpen_BMP(gdispImage *img) { gdispImagePrivate *priv; uint8_t hdr[2]; @@ -126,7 +139,7 @@ gdispImageError gdispImageOpen_BMP(gdispImage *img) { uint32_t offsetColorTable; /* Read the file identifier */ - if (img->io.fns->read(&img->io, hdr, 2) != 2) + if (gfileRead(img->f, hdr, 2) != 2) return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us /* Process the BITMAPFILEHEADER structure */ @@ -154,18 +167,18 @@ gdispImageError gdispImageOpen_BMP(gdispImage *img) { #endif /* Skip the size field and the 2 reserved fields */ - if (img->io.fns->read(&img->io, priv->buf, 8) != 8) + if (gfileRead(img->f, priv->buf, 8) != 8) goto baddatacleanup; /* Get the offset to the bitmap data */ - if (img->io.fns->read(&img->io, &priv->frame0pos, 4) != 4) + if (gfileRead(img->f, &priv->frame0pos, 4) != 4) goto baddatacleanup; CONVERT_FROM_DWORD_LE(priv->frame0pos); /* Process the BITMAPCOREHEADER structure */ /* Get the offset to the colour data */ - if (img->io.fns->read(&img->io, &offsetColorTable, 4) != 4) + if (gfileRead(img->f, &offsetColorTable, 4) != 4) goto baddatacleanup; CONVERT_FROM_DWORD_LE(offsetColorTable); offsetColorTable += 14; // Add the size of the BITMAPFILEHEADER @@ -175,7 +188,7 @@ gdispImageError gdispImageOpen_BMP(gdispImage *img) { img->priv->bmpflags |= BMP_V2; // Read the header - if (img->io.fns->read(&img->io, priv->buf, 12-4) != 12-4) + if (gfileRead(img->f, priv->buf, 12-4) != 12-4) goto baddatacleanup; // Get the width img->width = *(uint16_t *)(((uint8_t *)priv->buf)+0); @@ -224,7 +237,7 @@ gdispImageError gdispImageOpen_BMP(gdispImage *img) { priv->bmpflags |= BMP_V4; // Read the header - if (img->io.fns->read(&img->io, priv->buf, 40-4) != 40-4) + if (gfileRead(img->f, priv->buf, 40-4) != 40-4) goto baddatacleanup; // Get the width adword = *(uint32_t *)(((uint8_t *)priv->buf)+0); @@ -327,18 +340,18 @@ gdispImageError gdispImageOpen_BMP(gdispImage *img) { #if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE /* Load the palette tables */ if (priv->bmpflags & BMP_PALETTE) { - img->io.fns->seek(&img->io, offsetColorTable); + gfileSetPos(img->f, offsetColorTable); if (!(priv->palette = (color_t *)gdispImageAlloc(img, priv->palsize*sizeof(color_t)))) return GDISP_IMAGE_ERR_NOMEMORY; if (priv->bmpflags & BMP_V2) { for(aword = 0; aword < priv->palsize; aword++) { - if (img->io.fns->read(&img->io, &priv->buf, 3) != 3) goto baddatacleanup; + if (gfileRead(img->f, &priv->buf, 3) != 3) goto baddatacleanup; priv->palette[aword] = RGB2COLOR(((uint8_t *)priv->buf)[2], ((uint8_t *)priv->buf)[1], ((uint8_t *)priv->buf)[0]); } } else { for(aword = 0; aword < priv->palsize; aword++) { - if (img->io.fns->read(&img->io, &priv->buf, 4) != 4) goto baddatacleanup; + if (gfileRead(img->f, &priv->buf, 4) != 4) goto baddatacleanup; priv->palette[aword] = RGB2COLOR(((uint8_t *)priv->buf)[2], ((uint8_t *)priv->buf)[1], ((uint8_t *)priv->buf)[0]); } } @@ -349,15 +362,15 @@ gdispImageError gdispImageOpen_BMP(gdispImage *img) { #if GDISP_NEED_IMAGE_BMP_16 || GDISP_NEED_IMAGE_BMP_32 /* Load the bit masks */ if (priv->bmpflags & BMP_COMP_MASK) { - img->io.fns->seek(&img->io, offsetColorTable); - if (img->io.fns->read(&img->io, &priv->maskred, 4) != 4) goto baddatacleanup; + gfileSetPos(img->f, offsetColorTable); + if (gfileRead(img->f, &priv->maskred, 4) != 4) goto baddatacleanup; CONVERT_FROM_DWORD_LE(priv->maskred); - if (img->io.fns->read(&img->io, &priv->maskgreen, 4) != 4) goto baddatacleanup; + if (gfileRead(img->f, &priv->maskgreen, 4) != 4) goto baddatacleanup; CONVERT_FROM_DWORD_LE(priv->maskgreen); - if (img->io.fns->read(&img->io, &priv->maskblue, 4) != 4) goto baddatacleanup; + if (gfileRead(img->f, &priv->maskblue, 4) != 4) goto baddatacleanup; CONVERT_FROM_DWORD_LE(priv->maskblue); if (priv->bmpflags & BMP_V4) { - if (img->io.fns->read(&img->io, &priv->maskalpha, 4) != 4) goto baddatacleanup; + if (gfileRead(img->f, &priv->maskalpha, 4) != 4) goto baddatacleanup; CONVERT_FROM_DWORD_LE(priv->maskalpha); } else priv->maskalpha = 0; @@ -419,20 +432,6 @@ unsupportedcleanup: return GDISP_IMAGE_ERR_UNSUPPORTED; // Not supported } -void gdispImageClose_BMP(gdispImage *img) { - if (img->priv) { -#if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE - if (img->priv->palette) - gdispImageFree(img, (void *)img->priv->palette, img->priv->palsize*sizeof(color_t)); -#endif - if (img->priv->frame0cache) - gdispImageFree(img, (void *)img->priv->frame0cache, img->width*img->height*sizeof(pixel_t)); - gdispImageFree(img, (void *)img->priv, sizeof(gdispImagePrivate)); - img->priv = 0; - } - img->io.fns->close(&img->io); -} - static coord_t getPixels(gdispImage *img, coord_t x) { gdispImagePrivate * priv; color_t * pc; @@ -454,7 +453,7 @@ static coord_t getPixels(gdispImage *img, coord_t x) { len = 0; while(x < img->width && len <= BLIT_BUFFER_SIZE-32) { - if (img->io.fns->read(&img->io, &b, 4) != 4) + if (gfileRead(img->f, &b, 4) != 4) return 0; for(m=0x80; m; m >>= 1, pc++) @@ -499,7 +498,7 @@ static coord_t getPixels(gdispImage *img, coord_t x) { return len; } else if (priv->bmpflags & BMP_RLE_ABS) { while (priv->rlerun && len <= BLIT_BUFFER_SIZE-2 && x < img->width) { - if (img->io.fns->read(&img->io, &b, 1) != 1) + if (gfileRead(img->f, &b, 1) != 1) return 0; *pc++ = priv->palette[b[0] >> 4]; priv->rlerun--; @@ -514,8 +513,8 @@ static coord_t getPixels(gdispImage *img, coord_t x) { } if (priv->rlerun) // Return if we have more run to do return len; - if ((img->io.pos - priv->frame0pos)&1) { // Make sure we are on a word boundary - if (img->io.fns->read(&img->io, &b, 1) != 1) + if ((gfileGetPos(img->f) - priv->frame0pos)&1) { // Make sure we are on a word boundary + if (gfileRead(img->f, &b, 1) != 1) return 0; } } @@ -524,7 +523,7 @@ static coord_t getPixels(gdispImage *img, coord_t x) { priv->bmpflags &= ~(BMP_RLE_ENC|BMP_RLE_ABS); // There are always at least 2 bytes in an RLE code - if (img->io.fns->read(&img->io, &b, 2) != 2) + if (gfileRead(img->f, &b, 2) != 2) return 0; if (b[0]) { // Encoded mode @@ -541,7 +540,7 @@ static coord_t getPixels(gdispImage *img, coord_t x) { return len; } else if (b[1] == 2) { // Delta x, y // There are always at least 2 bytes in an RLE code - if (img->io.fns->read(&img->io, &b, 2) != 2) + if (gfileRead(img->f, &b, 2) != 2) return 0; priv->rlerun = b[0] + (uint16_t)b[1] * img->width; priv->rlecode = 0; // Who knows what color this should really be @@ -559,7 +558,7 @@ static coord_t getPixels(gdispImage *img, coord_t x) { uint8_t b[4]; while(x < img->width && len <= BLIT_BUFFER_SIZE-8) { - if (img->io.fns->read(&img->io, &b, 4) != 4) + if (gfileRead(img->f, &b, 4) != 4) return 0; *pc++ = priv->palette[b[0] >> 4]; @@ -599,7 +598,7 @@ static coord_t getPixels(gdispImage *img, coord_t x) { return len; } else if (priv->bmpflags & BMP_RLE_ABS) { while (priv->rlerun && len < BLIT_BUFFER_SIZE && x < img->width) { - if (img->io.fns->read(&img->io, &b, 1) != 1) + if (gfileRead(img->f, &b, 1) != 1) return 0; *pc++ = priv->palette[b[0]]; priv->rlerun--; @@ -608,8 +607,8 @@ static coord_t getPixels(gdispImage *img, coord_t x) { } if (priv->rlerun) // Return if we have more run to do return len; - if ((img->io.pos - priv->frame0pos)&1) { // Make sure we are on a word boundary - if (img->io.fns->read(&img->io, &b, 1) != 1) + if ((gfileGetPos(img->f) - priv->frame0pos)&1) { // Make sure we are on a word boundary + if (gfileRead(img->f, &b, 1) != 1) return 0; } } @@ -618,7 +617,7 @@ static coord_t getPixels(gdispImage *img, coord_t x) { priv->bmpflags &= ~(BMP_RLE_ENC|BMP_RLE_ABS); // There are always at least 2 bytes in an RLE code - if (img->io.fns->read(&img->io, &b, 2) != 2) + if (gfileRead(img->f, &b, 2) != 2) return 0; if (b[0]) { // Encoded mode @@ -635,7 +634,7 @@ static coord_t getPixels(gdispImage *img, coord_t x) { return len; } else if (b[1] == 2) { // Delta x, y // There are always at least 2 bytes in an RLE code - if (img->io.fns->read(&img->io, &b, 2) != 2) + if (gfileRead(img->f, &b, 2) != 2) return GDISP_IMAGE_ERR_BADDATA; priv->rlerun = b[0] + (uint16_t)b[1] * img->width; priv->rlecode = 0; // Who knows what color this should really be @@ -653,7 +652,7 @@ static coord_t getPixels(gdispImage *img, coord_t x) { uint8_t b[4]; while(x < img->width && len <= BLIT_BUFFER_SIZE-4) { - if (img->io.fns->read(&img->io, &b, 4) != 4) + if (gfileRead(img->f, &b, 4) != 4) return 0; *pc++ = priv->palette[b[0]]; @@ -675,7 +674,7 @@ static coord_t getPixels(gdispImage *img, coord_t x) { color_t r, g, b; while(x < img->width && len <= BLIT_BUFFER_SIZE-2) { - if (img->io.fns->read(&img->io, &w, 4) != 4) + if (gfileRead(img->f, &w, 4) != 4) return 0; CONVERT_FROM_WORD_LE(w[0]); CONVERT_FROM_WORD_LE(w[1]); @@ -720,7 +719,7 @@ static coord_t getPixels(gdispImage *img, coord_t x) { uint8_t b[3]; while(x < img->width && len < BLIT_BUFFER_SIZE) { - if (img->io.fns->read(&img->io, &b, 3) != 3) + if (gfileRead(img->f, &b, 3) != 3) return 0; *pc++ = RGB2COLOR(b[2], b[1], b[0]); x++; @@ -729,7 +728,7 @@ static coord_t getPixels(gdispImage *img, coord_t x) { if (x >= img->width) { // Make sure we have read a multiple of 4 bytes for the line - if ((x & 3) && img->io.fns->read(&img->io, &b, x & 3) != (x & 3)) + if ((x & 3) && gfileRead(img->f, &b, x & 3) != (x & 3)) return 0; } } @@ -743,7 +742,7 @@ static coord_t getPixels(gdispImage *img, coord_t x) { color_t r, g, b; while(x < img->width && len < BLIT_BUFFER_SIZE) { - if (img->io.fns->read(&img->io, &dw, 4) != 4) + if (gfileRead(img->f, &dw, 4) != 4) return 0; CONVERT_FROM_DWORD_LE(dw); if (priv->shiftred < 0) @@ -791,7 +790,7 @@ gdispImageError gdispImageCache_BMP(gdispImage *img) { return GDISP_IMAGE_ERR_NOMEMORY; /* Read the entire bitmap into cache */ - img->io.fns->seek(&img->io, priv->frame0pos); + gfileSetPos(img->f, priv->frame0pos); #if GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8_RLE priv->rlerun = 0; priv->rlecode = 0; @@ -847,7 +846,7 @@ gdispImageError gdispGImageDraw_BMP(GDisplay *g, gdispImage *img, coord_t x, coo } /* Start decoding from the beginning */ - img->io.fns->seek(&img->io, priv->frame0pos); + gfileSetPos(img->f, priv->frame0pos); #if GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8_RLE priv->rlerun = 0; priv->rlecode = 0; diff --git a/src/gdisp/image_gif.c b/src/gdisp/image_gif.c index 1ff72ff0..06f4ef6a 100644 --- a/src/gdisp/image_gif.c +++ b/src/gdisp/image_gif.c @@ -172,9 +172,9 @@ static gdispImageError startDecode(gdispImage *img) { // Local palette decode->maxpixel = priv->frame.palsize-1; decode->palette = (color_t *)(decode+1); - img->io.fns->seek(&img->io, priv->frame.pospal); + gfileSetPos(img->f, priv->frame.pospal); for(cnt = 0; cnt < priv->frame.palsize; cnt++) { - if (img->io.fns->read(&img->io, &decode->buf, 3) != 3) + if (gfileRead(img->f, &decode->buf, 3) != 3) goto baddatacleanup; decode->palette[cnt] = RGB2COLOR(decode->buf[0], decode->buf[1], decode->buf[2]); } @@ -188,8 +188,8 @@ static gdispImageError startDecode(gdispImage *img) { } // Get the initial lzw code size and values - img->io.fns->seek(&img->io, priv->frame.posimg); - if (img->io.fns->read(&img->io, &decode->bitsperpixel, 1) != 1 || decode->bitsperpixel >= MAX_CODE_BITS) + gfileSetPos(img->f, priv->frame.posimg); + if (gfileRead(img->f, &decode->bitsperpixel, 1) != 1 || decode->bitsperpixel >= MAX_CODE_BITS) goto baddatacleanup; decode->code_clear = 1 << decode->bitsperpixel; decode->code_eof = decode->code_clear + 1; @@ -273,8 +273,8 @@ static uint16_t getbytes(gdispImage *img) { // Get another code - a code is made up of decode->bitspercode bits. while (decode->shiftbits < decode->bitspercode) { // Get a byte - we may have to start a new data block - if ((!decode->blocksz && (img->io.fns->read(&img->io, &decode->blocksz, 1) != 1 || !decode->blocksz)) - || img->io.fns->read(&img->io, &bdata, 1) != 1) { + if ((!decode->blocksz && (gfileRead(img->f, &decode->blocksz, 1) != 1 || !decode->blocksz)) + || gfileRead(img->f, &bdata, 1) != 1) { // Pretend we got the EOF code - some encoders seem to just end the file decode->code_last = decode->code_eof; return cnt; @@ -302,8 +302,8 @@ static uint16_t getbytes(gdispImage *img) { if (code == decode->code_eof) { // Skip to the end of the data blocks do { - img->io.fns->seek(&img->io, img->io.pos+decode->blocksz); - } while (img->io.fns->read(&img->io, &decode->blocksz, 1) == 1 && decode->blocksz); + gfileSetPos(img->f, gfileGetPos(img->f)+decode->blocksz); + } while (gfileRead(img->f, &decode->blocksz, 1) == 1 && decode->blocksz); // Mark the end decode->code_last = decode->code_eof; @@ -398,8 +398,8 @@ static gdispImageError initFrame(gdispImage *img) { priv->dispose.height = priv->frame.height; // Check for a cached version of this image - for(cache=priv->cache; cache && cache->frame.posstart <= img->io.pos; cache=cache->next) { - if (cache->frame.posstart == img->io.pos) { + for(cache=priv->cache; cache && cache->frame.posstart <= (size_t)gfileGetPos(img->f); cache=cache->next) { + if (cache->frame.posstart == (size_t)gfileGetPos(img->f)) { priv->frame = cache->frame; priv->curcache = cache; return GDISP_IMAGE_ERR_OK; @@ -408,20 +408,20 @@ static gdispImageError initFrame(gdispImage *img) { // Get ready for a new image priv->curcache = 0; - priv->frame.posstart = img->io.pos; + priv->frame.posstart = gfileGetPos(img->f); priv->frame.flags = 0; priv->frame.delay = 0; priv->frame.palsize = 0; // Process blocks until we reach the image descriptor while(1) { - if (img->io.fns->read(&img->io, &blocktype, 1) != 1) + if (gfileRead(img->f, &blocktype, 1) != 1) return GDISP_IMAGE_ERR_BADDATA; switch(blocktype) { case 0x2C: //',' - IMAGE_DESC_RECORD_TYPE; // Read the Image Descriptor - if (img->io.fns->read(&img->io, priv->buf, 9) != 9) + if (gfileRead(img->f, priv->buf, 9) != 9) return GDISP_IMAGE_ERR_BADDATA; priv->frame.x = *(uint16_t *)(((uint8_t *)priv->buf)+0); CONVERT_FROM_WORD_LE(priv->frame.x); @@ -437,7 +437,7 @@ static gdispImageError initFrame(gdispImage *img) { priv->frame.flags |= GIFL_INTERLACE; // We are ready to go for the actual palette read and image decode - priv->frame.pospal = img->io.pos; + priv->frame.pospal = gfileGetPos(img->f); priv->frame.posimg = priv->frame.pospal+priv->frame.palsize*3; priv->frame.posend = 0; @@ -448,13 +448,13 @@ static gdispImageError initFrame(gdispImage *img) { case 0x21: //'!' - EXTENSION_RECORD_TYPE; // Read the extension type - if (img->io.fns->read(&img->io, &blocktype, 1) != 1) + if (gfileRead(img->f, &blocktype, 1) != 1) return GDISP_IMAGE_ERR_BADDATA; switch(blocktype) { case 0xF9: // EXTENSION - Graphics Control Block // Read the GCB - if (img->io.fns->read(&img->io, priv->buf, 6) != 6) + if (gfileRead(img->f, priv->buf, 6) != 6) return GDISP_IMAGE_ERR_BADDATA; // Check we have read a 4 byte data block and a data block terminator (0) if (((uint8_t *)priv->buf)[0] != 4 || ((uint8_t *)priv->buf)[5] != 0) @@ -485,7 +485,7 @@ static gdispImageError initFrame(gdispImage *img) { if (priv->flags & GIF_LOOP) goto skipdatablocks; // Read the Application header - if (img->io.fns->read(&img->io, priv->buf, 16) != 16) + if (gfileRead(img->f, priv->buf, 16) != 16) return GDISP_IMAGE_ERR_BADDATA; // Check we have read a 11 byte data block if (((uint8_t *)priv->buf)[0] != 11 && ((uint8_t *)priv->buf)[12] != 3) @@ -516,11 +516,11 @@ static gdispImageError initFrame(gdispImage *img) { // We don't understand this extension - just skip it by skipping data blocks skipdatablocks: while(1) { - if (img->io.fns->read(&img->io, &blocksz, 1) != 1) + if (gfileRead(img->f, &blocksz, 1) != 1) return GDISP_IMAGE_ERR_BADDATA; if (!blocksz) break; - img->io.fns->seek(&img->io, img->io.pos + blocksz); + gfileSetPos(img->f, gfileGetPos(img->f) + blocksz); } break; } @@ -537,7 +537,7 @@ static gdispImageError initFrame(gdispImage *img) { } // Seek back to frame0 - img->io.fns->seek(&img->io, priv->frame0pos); + gfileSetPos(img->f, priv->frame0pos); return GDISP_IMAGE_LOOP; default: // UNDEFINED_RECORD_TYPE; @@ -546,13 +546,34 @@ static gdispImageError initFrame(gdispImage *img) { } } +void gdispImageClose_GIF(gdispImage *img) { + gdispImagePrivate * priv; + imgcache * cache; + imgcache * ncache; + + priv = img->priv; + if (priv) { + // Free any stored frames + cache = priv->cache; + while(cache) { + ncache = cache->next; + gdispImageFree(img, (void *)cache, sizeof(imgcache)+cache->frame.width*cache->frame.height+cache->frame.palsize*sizeof(color_t)); + cache = ncache; + } + if (priv->palette) + gdispImageFree(img, (void *)priv->palette, priv->palsize*sizeof(color_t)); + gdispImageFree(img, (void *)img->priv, sizeof(gdispImagePrivate)); + img->priv = 0; + } +} + gdispImageError gdispImageOpen_GIF(gdispImage *img) { gdispImagePrivate *priv; uint8_t hdr[6]; uint16_t aword; /* Read the file identifier */ - if (img->io.fns->read(&img->io, hdr, 6) != 6) + if (gfileRead(img->f, hdr, 6) != 6) return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us /* Process the GIFFILEHEADER structure */ @@ -580,7 +601,7 @@ gdispImageError gdispImageOpen_GIF(gdispImage *img) { /* Process the Screen Descriptor structure */ // Read the screen descriptor - if (img->io.fns->read(&img->io, priv->buf, 7) != 7) + if (gfileRead(img->f, priv->buf, 7) != 7) goto baddatacleanup; // Get the width img->width = *(uint16_t *)(((uint8_t *)priv->buf)+0); @@ -596,7 +617,7 @@ gdispImageError gdispImageOpen_GIF(gdispImage *img) { goto nomemcleanup; // Read the global palette for(aword = 0; aword < priv->palsize; aword++) { - if (img->io.fns->read(&img->io, &priv->buf, 3) != 3) + if (gfileRead(img->f, &priv->buf, 3) != 3) goto baddatacleanup; priv->palette[aword] = RGB2COLOR(((uint8_t *)priv->buf)[0], ((uint8_t *)priv->buf)[1], ((uint8_t *)priv->buf)[2]); } @@ -604,7 +625,7 @@ gdispImageError gdispImageOpen_GIF(gdispImage *img) { priv->bgcolor = ((uint8_t *)priv->buf)[5]; // Save the fram0pos - priv->frame0pos = img->io.pos; + priv->frame0pos = gfileGetPos(img->f); // Read the first frame descriptor switch(initFrame(img)) { @@ -628,28 +649,6 @@ gdispImageError gdispImageOpen_GIF(gdispImage *img) { } } -void gdispImageClose_GIF(gdispImage *img) { - gdispImagePrivate * priv; - imgcache * cache; - imgcache * ncache; - - priv = img->priv; - if (priv) { - // Free any stored frames - cache = priv->cache; - while(cache) { - ncache = cache->next; - gdispImageFree(img, (void *)cache, sizeof(imgcache)+cache->frame.width*cache->frame.height+cache->frame.palsize*sizeof(color_t)); - cache = ncache; - } - if (priv->palette) - gdispImageFree(img, (void *)priv->palette, priv->palsize*sizeof(color_t)); - gdispImageFree(img, (void *)img->priv, sizeof(gdispImagePrivate)); - img->priv = 0; - } - img->io.fns->close(&img->io); -} - gdispImageError gdispImageCache_GIF(gdispImage *img) { gdispImagePrivate * priv; imgcache * cache; @@ -786,7 +785,7 @@ gdispImageError gdispImageCache_GIF(gdispImage *img) { } // We could be pedantic here but extra bytes won't hurt us while(getbytes(img)); - priv->frame.posend = cache->frame.posend = img->io.pos; + priv->frame.posend = cache->frame.posend = gfileGetPos(img->f); // Save everything priv->curcache = cache; @@ -1150,7 +1149,7 @@ gdispImageError gdispGImageDraw_GIF(GDisplay *g, gdispImage *img, coord_t x, coo } // We could be pedantic here but extra bytes won't hurt us while (getbytes(img)); - priv->frame.posend = img->io.pos; + priv->frame.posend = gfileGetPos(img->f); stopDecode(img); return GDISP_IMAGE_ERR_OK; @@ -1173,19 +1172,19 @@ delaytime_t gdispImageNext_GIF(gdispImage *img) { // We need to get to the end of this frame if (!priv->frame.posend) { // We don't know where the end of the frame is yet - find it! - img->io.fns->seek(&img->io, priv->frame.posimg+1); // Skip the code size byte too + gfileSetPos(img->f, priv->frame.posimg+1); // Skip the code size byte too while(1) { - if (img->io.fns->read(&img->io, &blocksz, 1) != 1) + if (gfileRead(img->f, &blocksz, 1) != 1) return TIME_INFINITE; if (!blocksz) break; - img->io.fns->seek(&img->io, img->io.pos + blocksz); + gfileSetPos(img->f, gfileGetPos(img->f) + blocksz); } - priv->frame.posend = img->io.pos; + priv->frame.posend = gfileGetPos(img->f); } // Seek to the end of this frame - img->io.fns->seek(&img->io, priv->frame.posend); + gfileSetPos(img->f, priv->frame.posend); // Read the next frame descriptor for(blocksz=0; blocksz < 2; blocksz++) { // 2 loops max to prevent cycling forever with a bad file diff --git a/src/gdisp/image_native.c b/src/gdisp/image_native.c index 7f249ae8..c458531e 100644 --- a/src/gdisp/image_native.c +++ b/src/gdisp/image_native.c @@ -33,11 +33,20 @@ typedef struct gdispImagePrivate { pixel_t buf[BLIT_BUFFER_SIZE]; } gdispImagePrivate; +void gdispImageClose_NATIVE(gdispImage *img) { + if (img->priv) { + if (img->priv->frame0cache) + gdispImageFree(img, (void *)img->priv->frame0cache, img->width * img->height * sizeof(pixel_t)); + gdispImageFree(img, (void *)img->priv, sizeof(gdispImagePrivate)); + img->priv = 0; + } +} + gdispImageError gdispImageOpen_NATIVE(gdispImage *img) { uint8_t hdr[HEADER_SIZE]; /* Read the 8 byte header */ - if (img->io.fns->read(&img->io, hdr, 8) != 8) + if (gfileRead(img->f, hdr, 8) != 8) return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us if (hdr[0] != 'N' || hdr[1] != 'I') @@ -60,16 +69,6 @@ gdispImageError gdispImageOpen_NATIVE(gdispImage *img) { return GDISP_IMAGE_ERR_OK; } -void gdispImageClose_NATIVE(gdispImage *img) { - if (img->priv) { - if (img->priv->frame0cache) - gdispImageFree(img, (void *)img->priv->frame0cache, img->width * img->height * sizeof(pixel_t)); - gdispImageFree(img, (void *)img->priv, sizeof(gdispImagePrivate)); - img->priv = 0; - } - img->io.fns->close(&img->io); -} - gdispImageError gdispImageCache_NATIVE(gdispImage *img) { size_t len; @@ -84,8 +83,8 @@ gdispImageError gdispImageCache_NATIVE(gdispImage *img) { return GDISP_IMAGE_ERR_NOMEMORY; /* Read the entire bitmap into cache */ - img->io.fns->seek(&img->io, FRAME0POS); - if (img->io.fns->read(&img->io, img->priv->frame0cache, len) != len) + gfileSetPos(img->f, FRAME0POS); + if (gfileRead(img->f, img->priv->frame0cache, len) != len) return GDISP_IMAGE_ERR_BADDATA; return GDISP_IMAGE_ERR_OK; @@ -112,12 +111,12 @@ gdispImageError gdispImageGDraw_NATIVE(GDisplay *g, gdispImage *img, coord_t x, /* Cycle through the lines */ for(;cy;cy--, y++) { /* Move to the start of the line */ - img->io.fns->seek(&img->io, pos); + gfileSetPos(img->f, pos); /* Draw the line in chunks using BitBlt */ for(mx = x, mcx = cx; mcx > 0; mcx -= len, mx += len) { // Read the data - len = img->io.fns->read(&img->io, + len = gfileRead(img->f, img->priv->buf, mcx > BLIT_BUFFER_SIZE ? (BLIT_BUFFER_SIZE*sizeof(pixel_t)) : (mcx * sizeof(pixel_t))) / sizeof(pixel_t); diff --git a/src/gfile/gfile.c b/src/gfile/gfile.c new file mode 100644 index 00000000..9edafea4 --- /dev/null +++ b/src/gfile/gfile.c @@ -0,0 +1,998 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * @file src/gfile/gfile.c + * @brief GFILE code. + * + */ + +#define GFILE_IMPLEMENTATION + +#include "gfx.h" + +#if GFX_USE_GFILE + +struct GFILE { + const struct GFILEVMT * vmt; + uint16_t flags; + #define GFILEFLG_OPEN 0x0001 // File is open + #define GFILEFLG_READ 0x0002 // Read the file + #define GFILEFLG_WRITE 0x0004 // Write the file + #define GFILEFLG_APPEND 0x0008 // Append on each write + #define GFILEFLG_BINARY 0x0010 // Treat as a binary file + #define GFILEFLG_DELONCLOSE 0x0020 // Delete on close + #define GFILEFLG_CANSEEK 0x0040 // Seek operations are valid + #define GFILEFLG_FAILONBLOCK 0x0080 // Fail on a blocking call + #define GFILEFLG_MUSTEXIST 0x0100 // On open file must exist + #define GFILEFLG_MUSTNOTEXIST 0x0200 // On open file must not exist + #define GFILEFLG_TRUNC 0x0400 // On open truncate the file + void * obj; + long int pos; +}; + +typedef struct GFILEVMT { + const struct GFILEVMT * next; + uint8_t flags; + #define GFSFLG_WRITEABLE 0x0001 + #define GFSFLG_CASESENSITIVE 0x0002 + #define GFSFLG_SEEKABLE 0x0004 + #define GFSFLG_FAST 0x0010 + #define GFSFLG_SMALL 0x0020 + #define GFSFLG_TEXTMODES 0x0040 + char prefix; + bool_t (*del) (const char *fname); + bool_t (*exists) (const char *fname); + long int (*filesize) (const char *fname); + bool_t (*ren) (const char *oldname, const char *newname); + bool_t (*open) (GFILE *f, const char *fname); + void (*close) (GFILE *f); + int (*read) (GFILE *f, void *buf, int size); + int (*write) (GFILE *f, const void *buf, int size); + bool_t (*setpos) (GFILE *f, long int pos); + long int (*getsize) (GFILE *f); + bool_t (*eof) (GFILE *f); +} GFILEVMT; + +// The chain of FileSystems +#define GFILE_CHAINHEAD 0 + +// The table of GFILE's +static GFILE gfileArr[GFILE_MAX_GFILES]; +GFILE *gfileStdIn; +GFILE *gfileStdOut; +GFILE *gfileStdErr; + +/** + * The order of the file-systems below determines the order + * that they are searched to find a file. + * The last defined is the first searched. + */ + +/******************************************************** + * The ChibiOS BaseFileStream VMT + ********************************************************/ +#if GFILE_NEED_CHIBIOSFS && GFX_USE_OS_CHIBIOS + #include "../src/gfile/inc_chibiosfs.c" +#endif + +/******************************************************** + * The Memory Pointer VMT + ********************************************************/ +#if GFILE_NEED_MEMFS + #include "../src/gfile/inc_memfs.c" +#endif + +/******************************************************** + * The RAM file-system VMT + ********************************************************/ +#if GFILE_NEED_RAMFS + #include "../src/gfile/inc_ramfs.c" +#endif + +/******************************************************** + * The FAT file-system VMT + ********************************************************/ +#ifndef GFILE_NEED_FATFS + #include "../src/gfile/inc_fatfs.c" +#endif + +/******************************************************** + * The native file-system + ********************************************************/ +#if GFILE_NEED_NATIVEFS + #include "../src/gfile/inc_nativefs.c" +#endif + +/******************************************************** + * The ROM file-system VMT + ********************************************************/ +#if GFILE_NEED_ROMFS + #include "../src/gfile/inc_romfs.c" +#endif + +/******************************************************** + * IO routines + ********************************************************/ + +/** + * The chain of file systems. + */ +static const GFILEVMT const * FsChain = GFILE_CHAINHEAD; + +/** + * The init routine + */ +void _gfileInit(void) { + #if GFILE_NEED_NATIVEFS + NativeStdIn.flags = GFILEFLG_OPEN|GFILEFLG_READ; + NativeStdIn.vmt = &FsNativeVMT; + NativeStdIn.obj = (void *)stdin; + NativeStdIn.pos = 0; + gfileStdIn = &NativeStdIn; + NativeStdOut.flags = GFILEFLG_OPEN|GFILEFLG_WRITE|GFILEFLG_APPEND; + NativeStdOut.vmt = &FsNativeVMT; + NativeStdOut.obj = (void *)stdout; + NativeStdOut.pos = 0; + gfileStdOut = &NativeStdOut; + NativeStdErr.flags = GFILEFLG_OPEN|GFILEFLG_WRITE|GFILEFLG_APPEND; + NativeStdErr.vmt = &FsNativeVMT; + NativeStdErr.obj = (void *)stderr; + NativeStdErr.pos = 0; + gfileStdErr = &NativeStdErr; + #endif +} + +void _gfileDeinit(void) +{ + /* ToDo */ +} + +bool_t gfileExists(const char *fname) { + const GFILEVMT *p; + + #if GFILE_ALLOW_DEVICESPECIFIC + if (fname[0] && fname[1] == '|') { + for(p = FsChain; p; p = p->next) { + if (p->prefix == fname[0]) + return p->exists && p->exists(fname+2); + } + return FALSE; + } + #endif + + for(p = FsChain; p; p = p->next) { + if (p->exists && p->exists(fname)) + return TRUE; + } + return FALSE; +} + +bool_t gfileDelete(const char *fname) { + const GFILEVMT *p; + + #if GFILE_ALLOW_DEVICESPECIFIC + if (fname[0] && fname[1] == '|') { + for(p = FsChain; p; p = p->next) { + if (p->prefix == fname[0]) + return p->del && p->del(fname+2); + } + return FALSE; + } + #endif + + for(p = FsChain; p; p = p->next) { + if (p->del && p->del(fname)) + return TRUE; + } + return FALSE; +} + +long int gfileGetFilesize(const char *fname) { + const GFILEVMT *p; + long int res; + + #if GFILE_ALLOW_DEVICESPECIFIC + if (fname[0] && fname[1] == '|') { + for(p = FsChain; p; p = p->next) { + if (p->prefix == fname[0]) + return p->filesize ? p->filesize(fname+2) : -1; + } + return -1; + } + #endif + + for(p = FsChain; p; p = p->next) { + if (p->filesize && (res = p->filesize(fname)) != -1) + return res; + } + return -1; +} + +bool_t gfileRename(const char *oldname, const char *newname) { + const GFILEVMT *p; + + #if GFILE_ALLOW_DEVICESPECIFIC + if ((oldname[0] && oldname[1] == '|') || (newname[0] && newname[1] == '|')) { + char ch; + + if (oldname[0] && oldname[1] == '|') { + ch = oldname[0]; + oldname += 2; + if (newname[0] && newname[1] == '|') { + if (newname[0] != ch) + // Both oldname and newname are fs specific but different ones. + return FALSE; + newname += 2; + } + } else { + ch = newname[0]; + newname += 2; + } + for(p = FsChain; p; p = p->next) { + if (p->prefix == ch) + return p->ren && p->ren(oldname, newname); + } + return FALSE; + } + #endif + + for(p = FsChain; p; p = p->next) { + if (p->ren && p->ren(oldname,newname)) + return TRUE; + } + return FALSE; +} + +static uint16_t mode2flags(const char *mode) { + uint16_t flags; + + switch(mode[0]) { + case 'r': + flags = GFILEFLG_READ|GFILEFLG_MUSTEXIST; + while (*++mode) { + switch(mode[0]) { + case '+': flags |= GFILEFLG_WRITE; break; + case 'b': flags |= GFILEFLG_BINARY; break; + } + } + return flags; + case 'w': + flags = GFILEFLG_WRITE|GFILEFLG_TRUNC; + while (*++mode) { + switch(mode[0]) { + case '+': flags |= GFILEFLG_READ; break; + case 'b': flags |= GFILEFLG_BINARY; break; + case 'x': flags |= GFILEFLG_MUSTNOTEXIST; break; + } + } + return flags; + case 'a': + flags = GFILEFLG_WRITE|GFILEFLG_APPEND; + while (*++mode) { + switch(mode[0]) { + case '+': flags |= GFILEFLG_READ; break; + case 'b': flags |= GFILEFLG_BINARY; break; + case 'x': flags |= GFILEFLG_MUSTNOTEXIST; break; + } + } + return flags; + } + return 0; +} + +static bool_t testopen(const GFILEVMT *p, GFILE *f, const char *fname) { + // If we want write but the fs doesn't allow it then return + if ((f->flags & GFILEFLG_WRITE) && !(p->flags & GFSFLG_WRITEABLE)) + return FALSE; + + // Try to open + if (!p->open || !p->open(f, fname)) + return FALSE; + + // File is open - fill in all the details + f->vmt = p; + f->pos = 0; + f->flags |= GFILEFLG_OPEN; + if (p->flags & GFSFLG_SEEKABLE) + f->flags |= GFILEFLG_CANSEEK; + return TRUE; +} + +GFILE *gfileOpen(const char *fname, const char *mode) { + uint16_t flags; + GFILE * f; + const GFILEVMT *p; + + // Get the requested mode + if (!(flags = mode2flags(mode))) + return 0; + + #if GFILE_ALLOW_DEVICESPECIFIC + if (fname[0] && fname[1] == '|') { + // First find an available GFILE slot. + for (f = gfileArr; f < &gfileArr[GFILE_MAX_GFILES]; f++) { + if (!(f->flags & GFILEFLG_OPEN)) { + // Try to open the file + f->flags = flags; + for(p = FsChain; p; p = p->next) { + if (p->prefix == fname[0]) + return testopen(p, f, fname+2) ? f : 0; + } + // File not found + break; + } + } + + // No available slot + return 0; + } + #endif + + // First find an available GFILE slot. + for (f = gfileArr; f < &gfileArr[GFILE_MAX_GFILES]; f++) { + if (!(f->flags & GFILEFLG_OPEN)) { + + // Try to open the file + f->flags = flags; + for(p = FsChain; p; p = p->next) { + if (testopen(p, f, fname)) + return f; + } + // File not found + break; + } + } + + // No available slot + return 0; +} + +#if GFILE_NEED_CHIBIOSFS && GFX_USE_OS_CHIBIOS + GFILE * gfileOpenBaseFileStream(void *BaseFileStreamPtr, const char *mode) { + GFILE * f; + + // First find an available GFILE slot. + for (f = gfileArr; f < &gfileArr[GFILE_MAX_GFILES]; f++) { + if (!(f->flags & GFILEFLG_OPEN)) { + // Get the flags + if (!(f->flags = mode2flags(mode))) + return 0; + + // If we want write but the fs doesn't allow it then return + if ((f->flags & GFILEFLG_WRITE) && !(FsCHIBIOSVMT.flags & GFSFLG_WRITEABLE)) + return 0; + + // File is open - fill in all the details + f->vmt = &FsCHIBIOSVMT; + f->obj = BaseFileStreamPtr; + f->pos = 0; + f->flags |= GFILEFLG_OPEN|GFILEFLG_CANSEEK; + return f; + } + } + + // No available slot + return 0; + } +#endif + +#if GFILE_NEED_MEMFS + GFILE * gfileOpenMemory(void *memptr, const char *mode) { + GFILE * f; + + // First find an available GFILE slot. + for (f = gfileArr; f < &gfileArr[GFILE_MAX_GFILES]; f++) { + if (!(f->flags & GFILEFLG_OPEN)) { + // Get the flags + if (!(f->flags = mode2flags(mode))) + return 0; + + // If we want write but the fs doesn't allow it then return + if ((f->flags & GFILEFLG_WRITE) && !(FsMemVMT.flags & GFSFLG_WRITEABLE)) + return 0; + + // File is open - fill in all the details + f->vmt = &FsMemVMT; + f->obj = memptr; + f->pos = 0; + f->flags |= GFILEFLG_OPEN|GFILEFLG_CANSEEK; + return f; + } + } + + // No available slot + return 0; + } +#endif + +void gfileClose(GFILE *f) { + if (!f || !(f->flags & GFILEFLG_OPEN)) + return; + if (f->vmt->close) + f->vmt->close(f); + f->flags = 0; +} + +size_t gfileRead(GFILE *f, void *buf, size_t len) { + size_t res; + + if (!f || (f->flags & (GFILEFLG_OPEN|GFILEFLG_READ)) != (GFILEFLG_OPEN|GFILEFLG_READ)) + return 0; + if (!f->vmt->read) + return 0; + if ((res = f->vmt->read(f, buf, len)) <= 0) + return 0; + f->pos += res; + return res; +} + +size_t gfileWrite(GFILE *f, const void *buf, size_t len) { + size_t res; + + if (!f || (f->flags & (GFILEFLG_OPEN|GFILEFLG_WRITE)) != (GFILEFLG_OPEN|GFILEFLG_WRITE)) + return 0; + if (!f->vmt->write) + return 0; + if ((res = f->vmt->write(f, buf, len)) <= 0) + return 0; + f->pos += res; + return res; +} + +long int gfileGetPos(GFILE *f) { + if (!f || !(f->flags & GFILEFLG_OPEN)) + return 0; + return f->pos; +} + +bool_t gfileSetPos(GFILE *f, long int pos) { + if (!f || !(f->flags & GFILEFLG_OPEN)) + return FALSE; + if (!f->vmt->setpos || !f->vmt->setpos(f, pos)) + return FALSE; + f->pos = pos; + return TRUE; +} + +long int gfileGetSize(GFILE *f) { + if (!f || !(f->flags & GFILEFLG_OPEN)) + return 0; + if (!f->vmt->getsize) + return 0; + return f->vmt->getsize(f); +} + +bool_t gfileEOF(GFILE *f) { + if (!f || !(f->flags & GFILEFLG_OPEN)) + return TRUE; + if (!f->vmt->eof) + return FALSE; + return f->vmt->eof(f); +} + +/******************************************************** + * String VMT routines + ********************************************************/ +#if GFILE_NEED_STRINGS && (GFILE_NEED_PRINTG || GFILE_NEED_SCANG) + #include + + // Special String VMT + static int StringRead(GFILE *f, void *buf, int size) { + // size must be 1 for a complete read + if (!((char *)f->obj)[f->pos]) + return 0; + ((char *)buf)[0] = ((char *)f->obj)[f->pos]; + return 1; + } + static int StringWrite(GFILE *f, const void *buf, int size) { + // size must be 1 for a complete write + ((char *)f->obj)[f->pos] = ((char *)buf)[0]; + return 1; + } + static const GFILEVMT StringVMT = { + 0, // next + 0, // flags + '_', // prefix + 0, 0, 0, 0, + 0, 0, StringRead, StringWrite, + 0, 0, 0, + }; +#endif + +/******************************************************** + * printg routines + ********************************************************/ +#if GFILE_NEED_PRINTG + #include + + #define MAX_FILLER 11 + #define FLOAT_PRECISION 100000 + + int fnprintg(GFILE *f, int maxlen, const char *fmt, ...) { + int res; + va_list ap; + + va_start(ap, fmt); + res = vfnprintg(f, maxlen, fmt, ap); + va_end(ap); + return res; + } + + static char *ltoa_wd(char *p, long num, unsigned radix, long divisor) { + int i; + char * q; + + if (!divisor) divisor = num; + + q = p + MAX_FILLER; + do { + i = (int)(num % radix); + i += '0'; + if (i > '9') + i += 'A' - '0' - 10; + *--q = i; + num /= radix; + } while ((divisor /= radix) != 0); + + i = (int)(p + MAX_FILLER - q); + do { + *p++ = *q++; + } while (--i); + + return p; + } + + int vfnprintg(GFILE *f, int maxlen, const char *fmt, va_list arg) { + int ret; + char *p, *s, c, filler; + int i, precision, width; + bool_t is_long, left_align; + long l; + #if GFILE_ALLOW_FLOATS + float f; + char tmpbuf[2*MAX_FILLER + 1]; + #else + char tmpbuf[MAX_FILLER + 1]; + #endif + + ret = 0; + if (maxlen < 0) + return 0; + if (!maxlen) + maxlen = -1; + + while (*fmt) { + if (*fmt != '%') { + gfileWrite(f, fmt, 1); + ret++; if (--maxlen) return ret; + fmt++; + continue; + } + fmt++; + + p = s = tmpbuf; + left_align = FALSE; + filler = ' '; + width = 0; + precision = 0; + + if (*fmt == '-') { + fmt++; + left_align = TRUE; + } + if (*fmt == '.') { + fmt++; + filler = '0'; + } + + while (1) { + c = *fmt++; + if (c >= '0' && c <= '9') + c -= '0'; + else if (c == '*') + c = va_arg(ap, int); + else + break; + width = width * 10 + c; + } + if (c == '.') { + while (1) { + c = *fmt++; + if (c >= '0' && c <= '9') + c -= '0'; + else if (c == '*') + c = va_arg(ap, int); + else + break; + precision = precision * 10 + c; + } + } + /* Long modifier.*/ + if (c == 'l' || c == 'L') { + is_long = TRUE; + if (*fmt) + c = *fmt++; + } + else + is_long = (c >= 'A') && (c <= 'Z'); + + /* Command decoding.*/ + switch (c) { + case 0: + return ret; + case 'c': + filler = ' '; + *p++ = va_arg(ap, int); + break; + case 's': + filler = ' '; + if ((s = va_arg(ap, char *)) == 0) + s = "(null)"; + if (precision == 0) + precision = 32767; + for (p = s; *p && (--precision >= 0); p++); + break; + case 'D': + case 'd': + if (is_long) + l = va_arg(ap, long); + else + l = va_arg(ap, int); + if (l < 0) { + *p++ = '-'; + l = -l; + } + p = ltoa_wd(p, l, 10, 0); + break; + #if GFILE_ALLOW_FLOATS + case 'f': + f = (float) va_arg(ap, double); + if (f < 0) { + *p++ = '-'; + f = -f; + } + l = f; + p = ltoa_wd(p, l, 10, 0); + *p++ = '.'; + l = (f - l) * FLOAT_PRECISION; + p = ltoa_wd(p, l, 10, FLOAT_PRECISION / 10); + break; + #endif + case 'X': + case 'x': + c = 16; + goto unsigned_common; + case 'U': + case 'u': + c = 10; + goto unsigned_common; + case 'O': + case 'o': + c = 8; + unsigned_common: + if (is_long) + l = va_arg(ap, long); + else + l = va_arg(ap, int); + p = ltoa_wd(p, l, c, 0); + break; + default: + *p++ = c; + break; + } + + i = (int)(p - s); + if ((width -= i) < 0) + width = 0; + if (left_align == FALSE) + width = -width; + if (width < 0) { + if (*s == '-' && filler == '0') { + gfileWrite(f, s++, 1); + ret++; if (--maxlen) return ret; + i--; + } + do { + gfileWrite(f, &filler, 1); + ret++; if (--maxlen) return ret; + } while (++width != 0); + } + while (--i >= 0) { + gfileWrite(f, s++, 1); + ret++; if (--maxlen) return ret; + } + while (width) { + gfileWrite(f, &filler, 1); + ret++; if (--maxlen) return ret; + width--; + } + } + return ret; + } + + #if GFILE_NEED_STRINGS + int snprintg(char *buf, int maxlen, const char *fmt, ...) { + int res; + GFILE f; + va_list ap; + + if (maxlen <= 1) { + if (maxlen == 1) + *buf = 0; + return 0; + } + f.flags = GFILEFLG_OPEN|GFILEFLG_WRITE; + f.vmt = &StringVMT; + f.pos = 0; + f.obj = buf; + va_start(ap, fmt); + res = vfnprintg(&f, maxlen-1, fmt, ap); + va_end(ap); + buf[res] = 0; + return res; + } + int vsnprintg(char *buf, int maxlen, const char *fmt, va_list arg) { + int res; + GFILE f; + + if (maxlen <= 1) { + if (maxlen == 1) + *buf = 0; + return 0; + } + f.flags = GFILEFLG_OPEN|GFILEFLG_WRITE; + f.vmt = &StringVMT; + f.pos = 0; + f.obj = buf; + res = vfnprintg(&f, maxlen-1, fmt, arg); + buf[res] = 0; + return res; + } + #endif +#endif + +/******************************************************** + * scang routines + ********************************************************/ +#if GFILE_NEED_SCANG + int fscang(GFILE *f, const char *fmt, ...) { + int res; + va_list ap; + + va_start(ap, fmt); + res = vfscang(f, fmt, ap); + va_end(ap); + return res; + } + + int vfscang(GFILE *f, const char *fmt, va_list arg) { + int res, width, size, base; + char c; + bool_t assign; + void *p; + + for(res = 0; *fmt; fmt++) { + switch(*fmt) { + case ' ': case '\t': case '\r': case '\n': case '\v': case '\f': + break; + + case '%': + fmt++; + assign = TRUE; + width = 0; + size = 1; + + if (*fmt == '*') { + fmt++; + assign = FALSE; + } + while(*fmt >= '0' && *fmt <= '9') + width = width * 10 + (*fmt++ - '0'); + if (*fmt == 'h') { + fmt++; + size = 0; + } else if (*fmt == 'l') { + fmt++; + size = 2; + } else if (*fmt == 'L') { + fmt++; + size = 3; + } + switch(*fmt) { + case 0: + return res; + case '%': + goto matchchar; + case 'c': + if (!width) { + while(1) { + if (!gfileRead(f, &c, 1)) return res; + switch(c) { + case ' ': case '\t': case '\r': + case '\n': case '\v': case '\f': continue; + } + break; + } + width = 1; + } else { + if (!gfileRead(f, &c, 1)) return res; + } + if (assign) { + p = va_arg(ap, char *); + res++; + *((char *)p)++ = c; + } + while(--width) { + if (!gfileRead(f, &c, 1)) return res; + if (assign) *((char *)p)++ = c; + } + break; + case 's': + while(1) { + if (!gfileRead(f, &c, 1)) return res; + switch(c) { + case ' ': case '\t': case '\r': + case '\n': case '\v': case '\f': continue; + } + break; + } + if (assign) { + p = va_arg(ap, char *); + res++; + *((char *)p)++ = c; + } + if (width) { + while(--width) { + if (!gfileRead(f, &c, 1)) { + if (assign) *((char *)p) = 0; + return res; + } + if (assign) *((char *)p)++ = c; + } + } else { + while(1) { + if (!gfileRead(f, &c, 1)) { + if (assign) *((char *)p) = 0; + return res; + } + switch(c) { + case ' ': case '\t': case '\r': + case '\n': case '\v': case '\f': break; + default: + if (assign) *((char *)p)++ = c; + continue; + } + break; + } + //ungetch(c); + } + if (assign) *((char *)p) = 0; + break; + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'b': + /* + while (isspace (*buf)) + buf++; + if (*s == 'd' || *s == 'u') + base = 10; + else if (*s == 'x') + base = 16; + else if (*s == 'o') + base = 8; + else if (*s == 'b') + base = 2; + if (!width) { + if (isspace (*(s + 1)) || *(s + 1) == 0) + width = strcspn (buf, ISSPACE); + else + width = strchr (buf, *(s + 1)) - buf; + } + strncpy (tmp, buf, width); + tmp[width] = '\0'; + buf += width; + if (!noassign) + atob (va_arg (ap, u_int32_t *), tmp, base); + } + if (!noassign) + count++; + */ + + #if GFILE_ALLOW_FLOATS + case 'e': case 'f': case 'g': + #endif + default: + return res; + } + + break; + + default: + matchchar: + while(1) { + if (!gfileRead(f, &c, 1)) return res; + switch(c) { + case ' ': case '\t': case '\r': + case '\n': case '\v': case '\f': continue; + } + break; + } + if (c != *fmt) return res; + break; + } + } + return res; + } + + #if GFILE_NEED_STRINGS + int sscang(const char *buf, const char *fmt, ...) { + int res; + GFILE f; + va_list ap; + + f.flags = GFILEFLG_OPEN|GFILEFLG_READ; + f.vmt = &StringVMT; + f.pos = 0; + f.obj = buf; + va_start(ap, fmt); + res = vfscang(&f, fmt, ap); + va_end(ap); + return res; + } + + int vsscang(const char *buf, const char *fmt, va_list arg) { + int res; + GFILE f; + + f.flags = GFILEFLG_OPEN|GFILEFLG_READ; + f.vmt = &StringVMT; + f.pos = 0; + f.obj = buf; + res = vfscang(&f, fmt, arg); + return res; + } + #endif +#endif + +/******************************************************** + * stdio emulation routines + ********************************************************/ +#if GFILE_NEED_STDIO + size_t gstdioRead(void * ptr, size_t size, size_t count, FILE *f) { + return gfileRead(f, ptr, size*count)/size; + } + size_t gstdioWrite(const void * ptr, size_t size, size_t count, FILE *f) { + return gfileWrite(f, ptr, size*count)/size; + } + int gstdioSeek(FILE *f, size_t offset, int origin) { + switch(origin) { + case SEEK_SET: + break; + case SEEK_CUR: + offset += f->pos; + break; + case SEEK_END: + offset += gfileGetSize(f); + break; + default: + return -1; + } + return gfileSetPos(f, offset) ? 0 : -1; + } + int gstdioGetpos(FILE *f, long int *pos) { + if (!(f->flags & GFILEFLG_OPEN)) + return -1; + *pos = f->pos; + return 0; + } +#endif + +#endif /* GFX_USE_GFILE */ diff --git a/src/gfile/gfile.mk b/src/gfile/gfile.mk new file mode 100644 index 00000000..381bd6f6 --- /dev/null +++ b/src/gfile/gfile.mk @@ -0,0 +1 @@ +GFXSRC += $(GFXLIB)/src/gfile/gfile.c diff --git a/src/gfile/inc_chibiosfs.c b/src/gfile/inc_chibiosfs.c new file mode 100644 index 00000000..87b8c110 --- /dev/null +++ b/src/gfile/inc_chibiosfs.c @@ -0,0 +1,46 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * This file is included by src/gfile/gfile.c + */ + +/******************************************************** + * The ChibiOS BaseFileStream file-system VMT + ********************************************************/ + +static void ChibiOSBFSClose(GFILE *f); +static int ChibiOSBFSRead(GFILE *f, void *buf, int size); +static int ChibiOSBFSWrite(GFILE *f, const void *buf, int size); +static bool_t ChibiOSBFSSetpos(GFILE *f, long int pos); +static long int ChibiOSBFSGetsize(GFILE *f); +static bool_t ChibiOSBFSEof(GFILE *f); + +static const GFILEVMT FsCHIBIOSVMT = { + 0, // next + GFSFLG_SEEKABLE|GFSFLG_WRITEABLE, // flags + 0, // prefix + 0, 0, 0, 0, + 0, ChibiOSBFSClose, ChibiOSBFSRead, ChibiOSBFSWrite, + ChibiOSBFSSetpos, ChibiOSBFSGetsize, ChibiOSBFSEof, +}; + +static void ChibiOSBFSClose(GFILE *f) { + chFileStreamClose(((BaseFileStream *)f->fd)); +} +static int ChibiOSBFSRead(GFILE *f, void *buf, int size) { + return chSequentialStreamRead(((BaseFileStream *)f->fd), (uint8_t *)buf, size); +} +static int ChibiOSBFSWrite(GFILE *f, const void *buf, int size) { + return chSequentialStreamWrite(((BaseFileStream *)f->fd), (uint8_t *)buf, size); +} +static bool_t ChibiOSBFSSetpos(GFILE *f, long int pos) { + chFileStreamSeek(((BaseFileStream *)f->fd), pos); + return TRUE; +} +static long int ChibiOSBFSGetsize(GFILE *f) { return chFileStreamGetSize(((BaseFileStream *)f->fd)); } +static bool_t ChibiOSBFSEof(GFILE *f) { return f->pos >= chFileStreamGetSize(((BaseFileStream *)f->fd)); } diff --git a/src/gfile/inc_fatfs.c b/src/gfile/inc_fatfs.c new file mode 100644 index 00000000..d49cfe7a --- /dev/null +++ b/src/gfile/inc_fatfs.c @@ -0,0 +1,15 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * This file is included by src/gfile/gfile.c + */ + +/******************************************************** + * The FAT file-system VMT + ********************************************************/ +#error "GFILE: FATFS Not implemented yet" diff --git a/src/gfile/inc_memfs.c b/src/gfile/inc_memfs.c new file mode 100644 index 00000000..434150d8 --- /dev/null +++ b/src/gfile/inc_memfs.c @@ -0,0 +1,43 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * This file is included by src/gfile/gfile.c + */ + +/******************************************************** + * The Memory pointer file-system VMT + ********************************************************/ + +#include + +static int MEMRead(GFILE *f, void *buf, int size); +static int MEMWrite(GFILE *f, const void *buf, int size); +static bool_t MEMSetpos(GFILE *f, long int pos); + +static const GFILEVMT FsMemVMT = { + 0, // next + GFSFLG_SEEKABLE|GFSFLG_WRITEABLE, // flags + 0, // prefix + 0, 0, 0, 0, + 0, 0, MEMRead, MEMWrite, + MEMSetpos, 0, 0, +}; + +static int MEMRead(GFILE *f, void *buf, int size) { + memcpy(buf, ((char *)f->obj)+f->pos, size); + return size; +} +static int MEMWrite(GFILE *f, const void *buf, int size) { + memcpy(((char *)f->obj)+f->pos, buf, size); + return size; +} +static bool_t MEMSetpos(GFILE *f, long int pos) { + (void) f; + (void) pos; + return TRUE; +} diff --git a/src/gfile/inc_nativefs.c b/src/gfile/inc_nativefs.c new file mode 100644 index 00000000..4ecb2004 --- /dev/null +++ b/src/gfile/inc_nativefs.c @@ -0,0 +1,102 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * This file is included by src/gfile/gfile.c + */ + +/******************************************************** + * The native file-system + ********************************************************/ + +#include +#include +#include +//#include + +static GFILE NativeStdIn; +static GFILE NativeStdOut; +static GFILE NativeStdErr; + +static bool_t NativeDel(const char *fname); +static bool_t NativeExists(const char *fname); +static long int NativeFilesize(const char *fname); +static bool_t NativeRen(const char *oldname, const char *newname); +static bool_t NativeOpen(GFILE *f, const char *fname); +static void NativeClose(GFILE *f); +static int NativeRead(GFILE *f, void *buf, int size); +static int NativeWrite(GFILE *f, const void *buf, int size); +static bool_t NativeSetpos(GFILE *f, long int pos); +static long int NativeGetsize(GFILE *f); +static bool_t NativeEof(GFILE *f); + +static const GFILEVMT FsNativeVMT = { + GFILE_CHAINHEAD, // next + #if defined(WIN32) || GFX_USE_OS_WIN32 + GFSFLG_TEXTMODES| + #else + GFSFLG_CASESENSITIVE| + #endif + GFSFLG_WRITEABLE|GFSFLG_SEEKABLE|GFSFLG_FAST, // flags + 'N', // prefix + NativeDel, NativeExists, NativeFilesize, NativeRen, + NativeOpen, NativeClose, NativeRead, NativeWrite, + NativeSetpos, NativeGetsize, NativeEof, +}; +#undef GFILE_CHAINHEAD +#define GFILE_CHAINHEAD &FsNativeVMT + +static void flags2mode(char *buf, uint16_t flags) { + if (flags & GFILEFLG_MUSTEXIST) + *buf = 'r'; + else if (flags & GFILEFLG_APPEND) + *buf = 'a'; + else + *buf = 'w'; + buf++; + if ((flags & (GFILEFLG_READ|GFILEFLG_WRITE)) == (GFILEFLG_READ|GFILEFLG_WRITE)) + *buf++ = '+'; + if (flags & GFILEFLG_BINARY) + *buf++ = 'b'; + if (flags & GFILEFLG_MUSTNOTEXIST) + *buf++ = 'x'; + *buf++ = 0; +} + +static bool_t NativeDel(const char *fname) { return remove(fname) ? FALSE : TRUE; } +static void NativeClose(GFILE *f) { fclose((FILE *)f->obj); } +static int NativeRead(GFILE *f, void *buf, int size) { return fread(buf, 1, size, (FILE *)f->obj); } +static int NativeWrite(GFILE *f, const void *buf, int size) { return fwrite(buf, 1, size, (FILE *)f->obj); } +static bool_t NativeSetpos(GFILE *f, long int pos) { return fseek((FILE *)f->obj, pos, SEEK_SET) ? FALSE : TRUE; } +static bool_t NativeEof(GFILE *f) { return feof((FILE *)f->obj) ? TRUE : FALSE; } +static bool_t NativeRen(const char *oldname, const char *newname) { return rename(oldname, newname) ? FALSE : TRUE; } +static bool_t NativeExists(const char *fname) { + // We define access this way so we don't have to include which may + // (and does under windows) contain conflicting definitions for types such as uint16_t. + extern int access(const char *pathname, int mode); + return access(fname, 0) ? FALSE : TRUE; +} +static long int NativeFilesize(const char *fname) { + struct stat st; + if (stat(fname, &st)) return -1; + return st.st_size; +} +static bool_t NativeOpen(GFILE *f, const char *fname) { + FILE *fd; + char mode[5]; + + flags2mode(mode, f->flags); + if (!(fd = fopen(fname, mode))) + return FALSE; + f->obj = (void *)fd; + return TRUE; +} +static long int NativeGetsize(GFILE *f) { + struct stat st; + if (fstat(fileno((FILE *)f->obj), &st)) return -1; + return st.st_size; +} diff --git a/src/gfile/inc_ramfs.c b/src/gfile/inc_ramfs.c new file mode 100644 index 00000000..b0f0d052 --- /dev/null +++ b/src/gfile/inc_ramfs.c @@ -0,0 +1,15 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * This file is included by src/gfile/gfile.c + */ + +/******************************************************** + * The RAM file-system VMT + ********************************************************/ +#error "GFILE: RAMFS Not implemented yet" diff --git a/src/gfile/inc_romfs.c b/src/gfile/inc_romfs.c new file mode 100644 index 00000000..49c1b173 --- /dev/null +++ b/src/gfile/inc_romfs.c @@ -0,0 +1,95 @@ +/* + * This file is subject to the terms of the GFX License. If a copy of + * the license was not distributed with this file, you can obtain one at: + * + * http://ugfx.org/license.html + */ + +/** + * This file is included by src/gfile/gfile.c + */ + +/******************************************************** + * The ROM file-system VMT + ********************************************************/ + +#include + +// What directory file formats do we understand +#define ROMFS_DIR_VER_MAX 0x0000 + +// Compression Formats +#define ROMFS_CMP_UNCOMPRESSED 0 + +typedef struct ROMFS_DIRENTRY { + uint16_t ver; // Directory Entry Version + uint16_t cmp; // Compression format + const struct ROMFS_DIRENTRY * next; // The next entry + const char * name; // The file name + long int size; // The file size + const char * file; // The file data +} ROMFS_DIRENTRY; + +#define ROMFS_DIRENTRY_HEAD 0 + +#include "romfs_files.h" + +static const ROMFS_DIRENTRY const *FsROMHead = ROMFS_DIRENTRY_HEAD; + +static bool_t ROMExists(const char *fname); +static long int ROMFilesize(const char *fname); +static bool_t ROMOpen(GFILE *f, const char *fname); +static void ROMClose(GFILE *f); +static int ROMRead(GFILE *f, void *buf, int size); +static bool_t ROMSetpos(GFILE *f, long int pos); +static long int ROMGetsize(GFILE *f); +static bool_t ROMEof(GFILE *f); + +static const GFILEVMT FsROMVMT = { + GFILE_CHAINHEAD, // next + GFSFLG_CASESENSITIVE|GFSFLG_SEEKABLE|GFSFLG_FAST, // flags + 'S', // prefix + 0, ROMExists, ROMFilesize, 0, + ROMOpen, ROMClose, ROMRead, 0, + ROMSetpos, ROMGetsize, ROMEof, +}; +#undef GFILE_CHAINHEAD +#define GFILE_CHAINHEAD &FsROMVMT + +static const ROMFS_DIRENTRY *ROMFindFile(const char *fname) { + const ROMFS_DIRENTRY *p; + + for(p = FsROMHead; p; p = p->next) { + if (p->ver <= ROMFS_DIR_VER_MAX && p->cmp == ROMFS_CMP_UNCOMPRESSED && !strcmp(p->name, fname)) + break; + } + return p; +} +static bool_t ROMExists(const char *fname) { return ROMFindFile(fname) != 0; } +static long int ROMFilesize(const char *fname) { + const ROMFS_DIRENTRY *p; + + if (!(p = ROMFindFile(fname))) return -1; + return p->size; +} +static bool_t ROMOpen(GFILE *f, const char *fname) { + const ROMFS_DIRENTRY *p; + + if (!(p = ROMFindFile(fname))) return FALSE; + f->obj = (void *)p; + return TRUE; +} +static void ROMClose(GFILE *f) { (void)f; } +static int ROMRead(GFILE *f, void *buf, int size) { + const ROMFS_DIRENTRY *p; + + p = (const ROMFS_DIRENTRY *)f->obj; + if (p->size - f->pos < size) + size = p->size - f->pos; + if (size <= 0) return 0; + memcpy(buf, p->file+f->pos, size); + return size; +} +static bool_t ROMSetpos(GFILE *f, long int pos) { return pos <= ((const ROMFS_DIRENTRY *)f->obj)->size; } +static long int ROMGetsize(GFILE *f) { return ((const ROMFS_DIRENTRY *)f->obj)->size; } +static bool_t ROMEof(GFILE *f) { return f->pos >= ((const ROMFS_DIRENTRY *)f->obj)->size; } diff --git a/src/gwin/gimage.c b/src/gwin/gimage.c index b1f8e078..e4032b96 100644 --- a/src/gwin/gimage.c +++ b/src/gwin/gimage.c @@ -139,14 +139,11 @@ GHandle gwinGImageCreate(GDisplay *g, GImageObject *gobj, GWindowInit *pInit) { return (GHandle)gobj; } -bool_t gwinImageOpenMemory(GHandle gh, const void* memory) { +bool_t gwinImageOpenGFile(GHandle gh, GFILE *f) { if (gdispImageIsOpen(&widget(gh)->image)) gdispImageClose(&widget(gh)->image); - if (!gdispImageSetMemoryReader(&widget(gh)->image, memory)) - return FALSE; - - if (gdispImageOpen(&widget(gh)->image) != GDISP_IMAGE_ERR_OK) + if ((gdispImageOpenGFile(&widget(gh)->image, f) & GDISP_IMAGE_ERR_UNRECOVERABLE)) return FALSE; if ((gh->flags & GWIN_FLG_VISIBLE)) { @@ -161,54 +158,6 @@ bool_t gwinImageOpenMemory(GHandle gh, const void* memory) { return TRUE; } -#if defined(WIN32) || GFX_USE_OS_WIN32 || GFX_USE_OS_LINUX || GFX_USE_OS_OSX || defined(__DOXYGEN__) -bool_t gwinImageOpenFile(GHandle gh, const char* filename) { - if (gdispImageIsOpen(&widget(gh)->image)) - gdispImageClose(&widget(gh)->image); - - if (!gdispImageSetFileReader(&widget(gh)->image, filename)) - return FALSE; - - if (gdispImageOpen(&widget(gh)->image) != GDISP_IMAGE_ERR_OK) - return FALSE; - - if ((gh->flags & GWIN_FLG_VISIBLE)) { - // Setting the clip here shouldn't be necessary if the redraw doesn't overdraw - // but we put it in for safety anyway - #if GDISP_NEED_CLIP - gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); - #endif - _redraw(gh); - } - - return TRUE; -} -#endif - -#if GFX_USE_OS_CHIBIOS || defined(__DOXYGEN__) -bool_t gwinImageOpenStream(GHandle gh, void *streamPtr) { - if (gdispImageIsOpen(&widget(gh)->image)) - gdispImageClose(&widget(gh)->image); - - if (!gdispImageSetBaseFileStreamReader(&widget(gh)->image, streamPtr)) - return FALSE; - - if (gdispImageOpen(&widget(gh)->image) != GDISP_IMAGE_ERR_OK) - return FALSE; - - if ((gh->flags & GWIN_FLG_VISIBLE)) { - // Setting the clip here shouldn't be necessary if the redraw doesn't overdraw - // but we put it in for safety anyway - #if GDISP_NEED_CLIP - gdispGSetClip(gh->display, gh->x, gh->y, gh->width, gh->height); - #endif - _redraw(gh); - } - - return TRUE; -} -#endif - gdispImageError gwinImageCache(GHandle gh) { return gdispImageCache(&widget(gh)->image); } diff --git a/tools/file2c/binaries/linux/file2c b/tools/file2c/binaries/linux/file2c index 403c7d8f..9d33e817 100755 Binary files a/tools/file2c/binaries/linux/file2c and b/tools/file2c/binaries/linux/file2c differ diff --git a/tools/file2c/binaries/windows/file2c.exe b/tools/file2c/binaries/windows/file2c.exe index 333b1138..28e9a329 100644 Binary files a/tools/file2c/binaries/windows/file2c.exe and b/tools/file2c/binaries/windows/file2c.exe differ diff --git a/tools/file2c/src/Makefile.mingw32 b/tools/file2c/src/Makefile.mingw32 new file mode 100644 index 00000000..470330bd --- /dev/null +++ b/tools/file2c/src/Makefile.mingw32 @@ -0,0 +1,10 @@ + +CC = i686-pc-mingw32-gcc +CFLAGS = -Wall -O2 + +all: + $(CC) $(CFLAGS) -o file2c.exe file2c.c + +clean: + @rm file2c.exe + diff --git a/tools/file2c/src/file2c.c b/tools/file2c/src/file2c.c index f7bc1e9c..f1415fe7 100644 --- a/tools/file2c/src/file2c.c +++ b/tools/file2c/src/file2c.c @@ -8,11 +8,14 @@ #include #include #include +#include +#include #ifdef WIN32 #include #endif -static unsigned char buf[1024]; +static unsigned char buf[1024]; +static char tname[FILENAME_MAX]; static char *filenameof(char *fname) { char *p; @@ -25,6 +28,13 @@ static char *filenameof(char *fname) { #endif p = strrchr(fname, '/'); if (p) fname = p+1; + return fname; +} + +static char *basenameof(char *fname) { + char *p; + + fname = filenameof(fname); p = strchr(fname, '.'); if (p) *p = 0; return fname; @@ -43,34 +53,35 @@ char * opt_progname; char * opt_inputfile; char * opt_outputfile; char * opt_arrayname; +char * opt_dirname; int opt_breakblocks; +int opt_romdir; char * opt_static; char * opt_const; FILE * f_input; FILE * f_output; unsigned blocknum; -size_t len; +size_t len, totallen; size_t i; /* Default values for our parameters */ - opt_progname = filenameof(argv[0]); - opt_inputfile = 0; - opt_outputfile = 0; - opt_arrayname = 0; - opt_breakblocks = 0; - opt_static = ""; - opt_const = ""; + opt_progname = basenameof(argv[0]); + opt_inputfile = opt_outputfile = opt_arrayname = opt_dirname = 0; + opt_breakblocks = opt_romdir = 0; + opt_static = opt_const = ""; /* Read the arguments */ while(*++argv) { if (argv[0][0] == '-') { while (*++(argv[0])) { switch(argv[0][0]) { - case '?': case 'h': goto usage; - case 'b': opt_breakblocks = 1; break; - case 'c': opt_const = "const "; break; - case 's': opt_static = "static "; break; - case 'n': opt_arrayname = *++argv; goto nextarg; + case '?': case 'h': goto usage; + case 'd': opt_romdir = 1; break; + case 'b': opt_breakblocks = 1; break; + case 'c': opt_const = "const "; break; + case 's': opt_static = "static "; break; + case 'n': opt_arrayname = *++argv; goto nextarg; + case 'f': opt_romdir = 1; opt_dirname = *++argv; goto nextarg; default: fprintf(stderr, "Unknown flag -%c\n", argv[0][0]); goto usage; @@ -82,20 +93,28 @@ size_t i; opt_outputfile = argv[0]; else { usage: - fprintf(stderr, "Usage:\n\t%s -?\n" - "\t%s [-bs] [-n name] [inputfile] [outputfile]\n" - "\t\t-?\tThis help\n" - "\t\t-h\tThis help\n" - "\t\t-b\tBreak the arrays for compilers that won't handle large arrays\n" - "\t\t-c\tDeclare the arrays as const (useful to ensure they end up in Flash)\n" - "\t\t-s\tDeclare the arrays as static\n" - "\t\t-n name\tUse \"name\" as the name of the array\n" + fprintf(stderr, "Usage:\n\n%s -?\n" + "%s [-dbcs] [-n name] [-f file] [inputfile] [outputfile]\n" + "\t-?\tThis help\n" + "\t-h\tThis help\n" + "\t-d\tAdd a directory entry for the ROM file system\n" + "\t-b\tBreak the arrays for compilers that won't handle large arrays\n" + "\t-c\tDeclare as const (useful to ensure they end up in Flash)\n" + "\t-s\tDeclare as static\n" + "\t-n name\tUse \"name\" as the name of the array\n" + "\t-f file\tUse \"file\" as the filename in the ROM directory entry\n" , opt_progname, opt_progname); return 1; } nextarg: ; } + /* Make sure we can generate a default directory name if required */ + if (opt_romdir && !opt_dirname && !opt_inputfile) { + fprintf(stderr, "If using -d you must either specify an input filename or use -f to specify a directory entry filename\n"); + goto usage; + } + /* Open the input file */ if (opt_inputfile) { f_input = fopen(opt_inputfile, @@ -129,43 +148,57 @@ size_t i; fprintf(f_output, "/**\n * This file was generated "); if (opt_inputfile) fprintf(f_output, "from \"%s\" ", opt_inputfile); fprintf(f_output, "using...\n *\n *\t%s", opt_progname); - if (opt_arrayname || opt_static[0] || opt_const[0] || opt_breakblocks) { + if (opt_arrayname || opt_static[0] || opt_const[0] || opt_breakblocks || opt_romdir) { fprintf(f_output, " -"); + if (opt_romdir) fprintf(f_output, "b"); if (opt_breakblocks) fprintf(f_output, "b"); if (opt_const[0]) fprintf(f_output, "c"); if (opt_static[0]) fprintf(f_output, "s"); if (opt_arrayname) fprintf(f_output, "n %s", opt_arrayname); + if (opt_dirname) fprintf(f_output, (opt_arrayname ? " -f %s" : "f %s"), opt_dirname); } if (opt_inputfile) fprintf(f_output, " %s", opt_inputfile); if (opt_outputfile) fprintf(f_output, " %s", opt_outputfile); fprintf(f_output, "\n *\n */\n"); /* - * Set the array name. - * We do this after printing opt_inputfile for the last time as we - * modify opt_inputfile in place to generate opt_arrayname. + * Set the array name and dir name */ if (!opt_arrayname) { if (opt_inputfile) - opt_arrayname = filenameof(opt_inputfile); - if (!opt_arrayname || !opt_arrayname[0]) - opt_arrayname = "filearray"; + opt_arrayname = basenameof(strcpy(tname, opt_inputfile)); + if (!opt_arrayname || !opt_arrayname[0]) { + srand(time(NULL)); + sprintf(tname, "filearray%u", rand()); + opt_arrayname = tname; + } } opt_arrayname = clean4c(opt_arrayname); + if (opt_romdir && !opt_dirname) + opt_dirname = filenameof(opt_inputfile); /* Read the file processing 1K at a time */ blocknum = 0; + totallen = 0; while((len = fread(buf, 1, sizeof(buf), f_input))) { if (!blocknum++) - fprintf(f_output, "%s%sunsigned char %s[] = {", opt_static, opt_const, opt_arrayname); + fprintf(f_output, "%s%schar %s[] = {", opt_static, opt_const, opt_arrayname); else if (opt_breakblocks) - fprintf(f_output, "\n};\n%s%sunsigned char %s_p%u[] = {", opt_static, opt_const, opt_arrayname, blocknum); + fprintf(f_output, "\n};\n%s%schar %s_p%u[] = {", opt_static, opt_const, opt_arrayname, blocknum); for(i = 0; i < len; i++) { fprintf(f_output, (i & 0x0F) ? " 0x%02X," : "\n\t0x%02X,", buf[i]); } + totallen += len; } fprintf(f_output, "\n};\n"); + /* Add the directory entry if required */ + if (opt_romdir) { + fprintf(f_output, "\n#ifdef ROMFS_DIRENTRY_HEAD\n"); + fprintf(f_output, "\t%s%sROMFS_DIRENTRY %s_dir = { 0, 0, ROMFS_DIRENTRY_HEAD, \"%s\", %u, %s };\n", opt_static, opt_const, opt_arrayname, opt_dirname, totallen, opt_arrayname); + fprintf(f_output, "\t#undef ROMFS_DIRENTRY_HEAD\n\t#define ROMFS_DIRENTRY_HEAD &%s_dir\n#endif\n", opt_arrayname); + } + /* Clean up */ if (ferror(f_input)) fprintf(stderr, "Input file read error\n");